Hi I'm writing a IOS api to fetch data from server using AFNetworking 3.x My Server does not support caching. This this server header
Connection →Keep-Alive
Content-Length →4603
Content-Type →text/html; charset=UTF-8
Date →Wed, 10 Aug 2016 04:03:56 GMT
Keep-Alive →timeout=5, max=100
Server →Apache/2.4.18 (Unix) OpenSSL/1.0.1e-fips PHP/5.4.45
X-Powered-By →PHP/5.4.45
I want to build custom API to enable cache (for example request and response should be cache for 10 second so that if user make same request, cache data is used. If cache expired app will then re download again)
My AFNetworking Singleton
#pragma mark - Shared singleton instance
+ (ApiClient *)sharedInstance {
static ApiClient *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:SINGPOST_BASE_URL] sessionConfiguration:sessionConfiguration];
return sharedInstance;
}
- (id)initWithBaseURL:(NSURL *)url
{
if ((self = [super initWithBaseURL:url])) {
self.requestSerializer = [AFHTTPRequestSerializer serializer];
}
return self;
}
My JSON request
- (void)sendJSONRequest:(NSMutableURLRequest *)request
success:(void (^)(NSURLResponse *response, id responseObject))success
failure:(void (^)(NSError *error))failure {
self.responseSerializer.acceptableContentTypes = [AFHTTPResponseSerializer serializer].acceptableContentTypes;
self.requestSerializer.timeoutInterval = 5;
self.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
[self setDataTaskWillCacheResponseBlock:^NSCachedURLResponse * _Nonnull(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSCachedURLResponse * _Nonnull proposedResponse) {
return [[NSCachedURLResponse alloc] initWithResponse:proposedResponse.response
data:proposedResponse.data
userInfo:proposedResponse.userInfo
storagePolicy:NSURLCacheStorageAllowed];
}];
NSURLSessionDataTask *dataTask = [ApiClient.sharedInstance dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error URL: %#",request.URL.absoluteString);
NSLog(#"Error: %#", error);
failure(error);
} else {
NSDictionary *jsonDict = (NSDictionary *) responseObject;
success(response,jsonDict);
NSLog(#"Success URL: %#",request.URL.absoluteString);
NSLog(#"Success %#",jsonDict);
}
}];
[dataTask resume];
}
How to modify the bellow code to enable caching request and response (I want response to be keep like 10 sec) (Or 1 day if no internet connection) Any help is much appreciate. Thanks!
[self setDataTaskWillCacheResponseBlock:^NSCachedURLResponse * _Nonnull(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSCachedURLResponse * _Nonnull proposedResponse) {
return [[NSCachedURLResponse alloc] initWithResponse:proposedResponse.response
data:proposedResponse.data
userInfo:proposedResponse.userInfo
storagePolicy:NSURLCacheStorageAllowed];
}];
Instead of
return [[NSCachedURLResponse alloc] initWithResponse:proposedResponse.response
do
NSMutableDictionary *dictionary = [proposedResponse.response.allHeaderFields mutableCopy];
dictionary[#"Cache-Control" = ...
NSHTTPURLResponse *modifiedResponse =
[[NSHTTPURLResponse alloc initWithURL:proposedResponse.response.URL
statusCode:proposedResponse.response.statusCode
HTTPVersion:#"HTTP/1.1"
headerFields:modifiedHeaders];
[modifiedResponse
return [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse
Note that it is not possible to retrieve the HTTP version from the original request. I'm pretty sure I filed a bug about that. I'm also pretty sure that it doesn't have any effect on anything, and I don't think it even gets stored in the underlying object, so it probably doesn't matter.
Related
Right now I am developing I little class that has a method for sending a POST request. This method is intended for returning a ResponseModel (which basically has two ivars: code, message), this model is going to be map from response.
I am using dataTaskWithRequest:urlRequest completionHandler: method. Like this:
+ (void)sendPOSTRequest1:(id)data withResponse:(void (^) (ResponseModel * data) )taskResponse {
NSError *error = nil;
NSMutableURLRequest * urlRequest = [self getRequestObject];
[urlRequest setHTTPMethod:#"POST"];
NSData * requestData = [self encodeAndEncrypt:data];
[urlRequest setHTTPBody:requestData];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session
dataTaskWithRequest:urlRequest
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
ResponseModel * responseModel = [NSKeyedUnarchiver
unarchivedObjectOfClass:[ResponseModel class]
fromData:data
error:&error];
taskResponse(responseModel);
}];
[dataTask resume];
}
And call the method this way:
DummyModel * dummy = [[DummyModel alloc] init];
__block ResponseModel * result = [[ResponseModel alloc] init];
[HTTPRequest sendPOSTRequest1:dummy withResponse:^(ResponseModel *data) {
result = data;
NSLog(#"data %#",data);
}];
// It`s not sure that the asyncronous request has already finished by this point
NSLog(#"POST result : %#",result);
My problem is that I do not want to execute a code in call back block because I need to wait for the response in order to return a ResponseModel and whoever is implementing this can receive the Model and make other stuff.
I been researching for using NSURLConnection because it has a method for executing Synchronous request, but now It´s deprecated, so I been wondering: is It a way I can wait for a response using what I have in the code ?
You can use GCD to implement synchronous request like this:
swift code
public static func requestSynchronousData(request: URLRequest) -> Data? {
var data: Data? = nil
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: request, completionHandler: {
taskData, _, error -> () in
data = taskData
if data == nil, let error = error {print(error)}
semaphore.signal()
})
task.resume()
_ = semaphore.wait(timeout: .distantFuture)
return data
}
Objective-C code
+ (NSData *)requestSynchronousData:(NSURLRequest *)request {
__block NSData * data = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable taskData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(#"%#", error);
}
data = taskData;
dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return data;
}
You can use dispatch_async to handle UI interaction inside the block
DummyModel * dummy = [[DummyModel alloc] init];
__block ResponseModel * result = [[ResponseModel alloc] init];
[HTTPRequest sendPOSTRequest1:dummy withResponse:^(ResponseModel *data) {
result = data;
dispatch_async(dispatch_get_main_queue(), ^{
// handle some ui interaction
});
NSLog(#"data %#",data);
}];
I am having this error when my app enter background.
NSURLConnection finished with error - code -1001 Task
<09B84034-9F73-4DB6-A685-D891B1B1068A>.<2> finished with error - code:
-1001
I am using this code
- (id<XCDYouTubeOperation>) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler
{
NSLog(#"Getting Video Identfifier");
if (!completionHandler)
#throw [NSException exceptionWithName:NSInvalidArgumentException reason:#"The `completionHandler` argument must not be nil." userInfo:nil];
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier];
operation.completionBlock = ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
if (operation.video || operation.error)
{
NSAssert(!(operation.video && operation.error), #"One of `video` or `error` must be nil.");
completionHandler(operation.video, operation.error);
}
else
{
NSAssert(operation.isCancelled, #"Both `video` and `error` can not be nil if the operation was not canceled.");
}
operation.completionBlock = nil;
#pragma clang diagnostic pop
}];
};
NSLog(#"Operation - %#", operation ) ;
[self.queue addOperation:operation];
return operation;
}`
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
// [task resume];
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
self.requestType = requestType;
}
#pragma mark - Response Dispatch
- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType
{
NSLog(#"XCDDRequestType - ",requestType);
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR(""));
// Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html
NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: #"";
NSAssert(responseString.length > 0, #"Failed to decode response from %# (response.textEncodingName = %#, data.length = %#)", response.URL, response.textEncodingName, #(data.length));
XCDYouTubeLogVerbose(#"Response: %#\n%#", response, responseString);
switch (requestType)
{
case XCDYouTubeRequestTypeGetVideoInfo:
[self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response];
break;
case XCDYouTubeRequestTypeWatchPage:
[self handleWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeEmbedPage:
[self handleEmbedWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeJavaScriptPlayer:
[self handleJavaScriptPlayerWithScript:responseString];
break;
}
}
This code will automatically run in background but after a few minutes it will stop and gives me the above error. How to fix this ?
EDIT 1 (Vinay Kiran Method) #
i changed the nsurlsessionconfiguration to background.
- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier
{
if (!(self = [super init]))
return nil; // LCOV_EXCL_LINE
_videoIdentifier = videoIdentifier ?: #"";
_languageIdentifier = languageIdentifier ?: #"en";
// _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"YouTubeID"];
_session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
_operationStartSemaphore = dispatch_semaphore_create(0);
NSLog(#"Initialize the Video Identifier");
return self;
}
then change the completion handler since background it will give this error if i use handler
Swift: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NEWLY ADDED
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
[task resume];
// self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
// {
// if (self.isCancelled)
// return;
//
// if (error)
// [self handleConnectionError:error];
// else
// [self handleConnectionSuccessWithData:data response:response requestType:requestType];
// }];
// [self.dataTask resume];
self.requestType = requestType;
}
the problem now is that i originally use this
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
which handleConnectionSuccessWithData will take in data, response and request type. Now i don't know where can i get the data, response and request type if i use backgroundSessionConfigurationWithIdentifier.
Use background thread instead of the main queue
backgroundSessionConfigurationWithIdentifier:
For reference
https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1407496-backgroundsessionconfigurationwi?language=objc
I am new to Objective C and iOS development in general. I am trying to create an app that would make an http request and display the contents on a label.
When I started testing I noticed that the label was blank even though my logs showed that I had data back. Apparently this happens because the the response is not ready when the label text gets updated.
I put a loop on the top to fix this but I am almost sure there's got to be a better way to deal with this.
ViewController.m
- (IBAction)buttonSearch:(id)sender {
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: #"https://en.wiktionary.org/wiki/incredible"];
//I put this here to give some time for the url session to comeback.
int count;
while (http.responseText ==nil) {
self.outputLabel.text = [NSString stringWithFormat: #"Getting data %i ", count];
}
self.outputLabel.text = http.responseText;
}
HttpRequest.h
#import <Foundation/Foundation.h>
#interface HttpRequest : NSObject
#property (strong, nonatomic) NSString *responseText;
- (void) sendRequestFromURL: (NSString *) url;
- (NSString *) getElementBetweenText: (NSString *) start andText: (NSString *) end;
#end
HttpRequest.m
#implementation HttpRequest
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
}];
[task resume];
}
Thanks a lot for the help :)
Update
After reading a lot for the very useful comments here I realized that I was missing the whole point. So technically the NSURLSessionDataTask will add task to a queue that will make the call asynchronously and then I have to provide that call with a block of code I want to execute when the thread generated by the task has been completed.
Duncan thanks a lot for the response and the comments in the code. That helped me a lot to understand.
So I rewrote my procedures using the information provided. Note that they are a little verbose but, I wanted it like that understand the whole concept for now. (I am declaring a code block rather than nesting them)
HttpRequest.m
- (void) sendRequestFromURL: (NSString *) url
completion:(void (^)(NSString *, NSError *))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Create a block to handle the background thread in the dispatch method.
void (^runAfterCompletion)(void) = ^void (void) {
if (error) {
completionBlock (nil, error);
} else {
NSString *dataText = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
completionBlock(dataText, error);
}
};
//Dispatch the queue
dispatch_async(dispatch_get_main_queue(), runAfterCompletion);
}];
[task resume];
}
ViewController.m
- (IBAction)buttonSearch:(id)sender {
NSString *const myURL = #"https://en.wiktionary.org/wiki/incredible";
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: myURL
completion: ^(NSString *str, NSError *error) {
if (error) {
self.outputText.text = [error localizedDescription];
} else {
self.outputText.text = str;
}
}];
}
Please feel free to comment on my new code. Style, incorrect usage, incorrect flow; feedback is very important in this stage of learning so I can become a better developer :)
Again thanks a lot for the replies.
You know what, use AFNetworking to save your life.
Or just modify your HttpRequest's sendRequestFromURL:
- (void)sendRequestFromURL:(NSString *)url completion:(void(^)(NSString *str, NSError *error))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
completionBlock(nil, error);
} else {
completionBlock([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], error);
}
});
}];
[task resume];
}
and invoke like this
[http sendRequestFromURL:#"https://en.wiktionary.org/wiki/incredible" completion:^(NSString *str, NSError *error) {
if (!error) {
self.outputLabel.text = str;
}
}];
Rewrite your sendRequestFromURL function to take a completion block:
- (void) sendRequestFromURL: (NSString *) url
completion: (void (^)(void)) completion
{
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
if (completion != nil)
{
//The data task's completion block runs on a background thread
//by default, so invoke the completion handler on the main thread
//for safety
dispatch_async(dispatch_get_main_queue(), completion);
}
}];
[task resume];
}
Then, when you call sendRequestFromURL, pass in the code you want to run when the request is ready as the completion block:
[self.sendRequestFromURL: #"http://www.someURL.com&blahblahblah",
completion: ^
{
//The code that you want to run when the data task is complete, using
//self.responseText
}];
//Do NOT expect the result to be ready here. It won't be.
The code above uses a completion block with no parameters because your code saved the response text to an instance variable. It would be more typical to pass the response data and the NSError as parameters to the completion block. See #Yahoho's answer for a version of sendRequestFromURL that takes a completion block with a result string and an NSError parameter).
(Note: I wrote the code above in the SO post editor. It probably has a few syntax errors, but it's intended as a guide, not code you can copy/paste into place. Objective-C block syntax is kinda nasty and I usually get it wrong the first time at least half the time.)
If you want easy way then Don't make separate class for call webservice. Just make meethod in viewController.m instead. I mean write sendRequestFromURL in your viewController.m and update your label's text in completion handler something like,
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
self.outputLabel.text = self.responseText;
})
}];
[task resume];
}
The question AFNetworking : How to know if response is using cache or not ? 304 or 200 had been answered well for AFNetworking 2.x. But how do you do the same thing in 3.x?
It's very useful to know whether resources were returned from cache or from the network while debugging.
You can follow the same approach with AFNetworking3.0.
BOOL __block responseFromCache = YES; // yes by default
[self setDataTaskWillCacheResponseBlock:^NSCachedURLResponse * _Nonnull(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSCachedURLResponse * _Nonnull proposedResponse) {
responseFromCache = NO;
NSLog(#"Sending back to cache response");
NSCachedURLResponse * responseCached;
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) [proposedResponse response];
if (dataTask.originalRequest.cachePolicy == NSURLRequestUseProtocolCachePolicy) {
NSDictionary *headers = httpResponse.allHeaderFields;
NSString * cacheControl = [headers valueForKey:#"Cache-Control"];
NSString * expires = [headers valueForKey:#"Expires"];
if (cacheControl == nil && expires == nil) {
NSLog(#"Server does not provide info for cache policy");
responseCached = [[NSCachedURLResponse alloc] initWithResponse:dataTask.response
data:proposedResponse.data
userInfo:#{ #"response" : dataTask.response, #"proposed" : proposedResponse.data }
storagePolicy:NSURLCacheStorageAllowed];
}
}
return responseCached;
}];
[self wb_GET:url parameters:nil headerFields:additionalHeadersDict success:^(NSURLSessionDataTask *task, id responseObject) {
if (responseFromCache) {
// response was returned from cache
NSLog(#"RESPONSE FROM CACHE: %#", responseObject);
}
handler(responseObject, nil);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
handler(nil, error);
}];
Besides that you can also implement below delegate method to your AFHTTPSessionManager subclass.
- (void)baseSuccessWithResponseObject:(id)responseObject sessionTask: (NSURLSessionDataTask *) task validationHandler:(void(^)(id responseObject, NSError *error))handler{}
I'm having trouble getting a simple implementation of NSURLCache working on iOS8. It's my understanding that once a shared cache is created, it automatically caches data requests with the proper cache policy. No configuration required unless you want to customize behavior. Is this correct?
I've included a simplified version of my code below. The cache is created in AppDelegate, and the TableViewController that needs the data uses the APICaller object to make the call. The request is using NSURLRequestReturnCacheDataElseLoad, as this information doesn't need to be updated frequently.
If I'm way off the mark here. What's the next step? The data received is 95KB.
AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
return YES;
}
TableViewController:
- (void)viewDidLoad {
APICaller *apiCaller = [APICaller alloc] init];
[apiCaller makeAPICallWithCompletionHandler:^(NSArray *result, NSError *error){
if (error) {
// Handle error
} else {
self.property = [result mutableCopy];
[self.tableView reloadData];
}
}
}
APICaller:
- (void)makeAPICallWithCompletionHandler:(void(^)(NSArray *result, NSError *error))completionHandler
{
NSString *urlString = [NSString stringWithFormat#"https://api.apiwebsite.com/json/query?key=#", API_KEY];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10];
NSURLSessionConfiguration *config = [NSURLSession defaultSessionConfiguration];
self.urlSession = [URLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionDataTask *dataTask = [self.URLSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"There was an error");
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil, error);
});
} else {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
NSArray *sortedResult = [dict[#"result"] sortedArrayUsingDescriptors:sortDescriptors];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(sortedResponse, error);
});
}
}];
[dataTask resume];
}
According to this post and this post NSURLCache is broken in iOS 8 when using NSURLSession.
According to the first post I linked, NSURLConnection still seems to work. I remember seeing in one of the comments that this was still the case as of early Feb, but I haven't had a chance to investigate myself.
Best of luck.