UIImage with NSData - image compression dependancy - ios

I understand the need of image compression while downloading images when connected to a 3G network, but I am getting really bad looking images... I'm caching downloaded images and I realized that the quality of the images depends on the active connection. My code:
KTMember *member = [[DataManager sharedManager] getMemberWithId:memberId];
if (member) {
NSLog(#"caching member %d locally",member.memberId);
memberImg = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:member.imageUrl]]];
[[DataManager sharedManager] saveImageToDocuments:memberImg withId:memberId];
return memberImg;
} else {
return nil;
}
So the question is - is there any way of overriding the image compression even though the active network is 3G?
Thanks

There is no global mechanism that adaptively increases image compression for slow connections. What you describe would require custom code on the server, and would vary from server to server.
What service is providing these images?

EDIT: Thank you for fixing my answer, There are some mechanism of image compressing by Verizon network optimization.
I think,The quality of image, in the term of byte stream, depend on the server provide whether or not compression.
But there is some solution. you can also implement NSURLConnectionDataDelegate to handle the data from URL request with Thread Programming. There is interesting method :
/** connection:didReceiveResponse: is called when
* enough data has been read to construct an
* NSURLResponse object. In the event of a protocol
* which may return multiple responses (such as HTTP
* multipart/x-mixed-replace) the delegate should be
* prepared to inspect the new response and make
* itself ready for data callbacks as appropriate.
**/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
/** connection:didReceiveData: is called with a single
* immutable NSData object to the delegate,
* representing the next portion of the data loaded
* from the connection. This is the only guaranteed
* for the delegate to receive the data from the
* resource load
**/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
/** connection:willCacheResponse: gives the delegate
* an opportunity to inspect and modify the
* NSCachedURLResponse which will be cached by the
* loader if caching is enabled for the original
* NSURLRequest. Returning nil from this delegate
* will prevent the resource from being cached. Note
* that the -data method of the cached response may
* return an autoreleased in-memory copy of the true
* data, and should not be used as an alternative to
* receiving and accumulating the data through
* connection:didReceiveData
**/
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse;
/** connectionDidFinishLoading: is called when all
* connection processing has completed successfully,
* before the delegate is released by the
* connection
**/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
You can also manage your data in didReceiveData with accumulate each incoming data and when complete downloading , In connectionDidFinishLoading , you could deal with the NSData of image that you receive all.
Hope it helps you.

Related

Cache strategy, AFNetworking

I download images and use it in my app. I have more then 100 images and I use it not all at a time. I will use cache. Then I load all images I save it to cache. Then I will go to the other parts of the app and I will use some that images.
I not really understand how the ios cache works and I have a question: after load all images and saving it to cache, how should I use this images, I mean, I need load them from cache or use instances of images that I have before saving it ti cache ?
And what caching strategy is more useful with AFNetworking 2.0?
AFNetworking has in-memory caching for images. If you have 100 images it could appropriate for you, could be not.
You should also read about NSURLCache here - http://nshipster.com/nsurlcache/.
If you use NSURLCache you do not have to think about caching - when you start downloading something that is already cached, system will just give you downloaded file.
UPDATE:
Time expiration for downloaded data is set by server in response. But you can edit or ignore it by NSURLConnectionDelegate method. Example:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)[cachedResponse response];
// Look up the cache policy used in our request
if([connection currentRequest].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 expiration information and we are using NSURLRequestUseProtocolCachePolicy");
return nil; // don't cache this
}
}
return cachedResponse;
}

NSCachedURLResponse willCacheResponse does not get called

We have set up a simple NSURLConnection and NSURLCache as per the abbreviated code snippet below. We have made sure, that the server (localhost:9615) returns the following caching headers:
ETag : abcdefgh
Cache-Control : public, max-age=60
However, the willCacheResponse delegate method is never called. Any idea?
Code:
// In the app delegate
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
// Triggered by a UIButton
- (IBAction)sendRequest:(UIButton *)sender {
NSLog(#"sendRequest");
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://localhost:9615"] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
NSMutableData *receivedData = [NSMutableData dataWithCapacity: 0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
receivedData = nil;
}
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
NSLog(#"willCacheResponse");
// ...
}
Cocoa applies all sorts of criteria to determine whether it can cache. For example, in my experience, you will not see willCacheResponse called if the size of the response exceeds roughly 5% of the persistent storage cache size. I've also seen others claim that if max-age is smaller than 1835400, it won't cache either (this is not my experience, but perhaps older iOS versions suffered from this). Obviously, the request has to be a http or https request, not ftp or file request.
If my cache is large enough (and my response correctly supplies Cache-Control and Content-Type), I find that the cache works properly. I only wish that Apple would clearly articulate the criteria being applied (either in documentation or return codes) as I and others have wasted hours diagnosing cache failures only to realize that Cocoa was applying some secret rules.
Note, if NSURLConnection was, itself, satisfied by retrieving it from cache, willCacheResponse will not be called. And, of course ensure that didFailWithError was not called.

Objective-C NSURLConnection didReceiveData creating bad JSON in NSData

I'm currently attempting to stream data from Twitter using their streaming API's. I've attached the code below for creating my NSData and appending to it on didReceiveData. For some reason, every time didReceiveData gets a response from Twitter, it's appended on as a new JSON root into the NSData, so when I attempt to parse the NSData into a JSON structure, it blows up.
I couldn't figure out what was going on and posted the JSON into a validator and it noted that there were multiple roots in the JSON. How can I modify the code to continue to append to the existing JSON root? Or is there an easier way to go about deserializing into JSON when there's multiple JSON entries in the NSData?
- (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
NSLog(#"Did receive response");
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
NSLog(#"Did Receive data");
[_responseData appendData:data];
}
I think what you need is just some extra logic to handle the real-time nature of this. Use your NSMutableData as a container to continue receiving data, but at the end of each batch you should scan the data object for all valid objects, build them, and store them into a different object that holds all the built json objects. In this example lets assume you have this ivar: NSMutableArray *_wholeObjects
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
NSLog(#"Did Receive data");
[_responseData appendData:data];
[self buildWholeObjects]
}
- (void) buildWholeObjects {
NSArray *rootObjects = <#business logic to return one whole JSON object per array element, or return nil if none found#>
if (rootObjects != nil) {
NSUInteger bytesExtracted = 0;
for (rootObject in rootObjects) {
[_wholeObjects addElement:rootObject];
bytesExtracted += rootObject.length;
}
NSData *remainingData = [_responseData subdataWithRange:NSMakeRange(bytesExtracted, _responseData.length - bytesExtracted];
[_responseData setData:remainingData];
}
}
After doing this only access the objects in _wholeObjects, where each element represents a fully valid JSON object that you can deserialize or read in any way you need.
Just for the sake of clarity, lets say the first NSData represents:
{"a":"2"}{"c":"5
When you process it _wholeObjects will have one element representing {"a":"2"}, and _responseData will now be {"c":"5
Then the next stream of data should continue on the object. Lets say the second NSData is:
"}
Now _responseData is {"c":"5"} because we appended the new message onto the remaining old message. We build this one out, and get a second element in _wholeObjects, and _responseData will be empty and ready to receive the next set of data.
Hope that helps some. I think the hard part for you is going to be determining how much of the _responseData is considered a valid JSON object. If they are simple enough you can just count the number of opening {/[ to closing }/] and pull that substring out.
Just to follow up on this topic for anyone dealing with the same thing: I ended up using SBJson which has support for streaming. http://superloopy.io/json-framework/

Collecting data from NSURLConnection

I have the worst internet connection atm, so sorry if this has been asked before..
I have an NSURLConnection for getting some json data. Until now it worked perfectly fine to use the delegate method didReceiveData:(NSData*)data to save the received data. I am downloading data from at least seven different pages at the same time. Today, after updati g on of the json-pages to contain more data, the NSData object seemed corrupt. I have recently been told that this delegate does not return the whole data, and thus corrupting my information.
Is there another delegate like the didFinish only it also returns the full complete object? Or do I have to do this myself, like merging two NSData's?
Sorry for stupidity, and grammatical errors are dedicated to iPhone auto-correct.
You must never, ever rely on didReceiveData: returning the full data, because it will break one day. You have to collect your chunks of data in an NSMutableData:
NSMutableData *d = [[NSMutableData alloc] init];
- (void)connection:(NSURLConnection *)c didReceiveData:(NSData *)data
{
[d appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn
{
// 'd' now contains the entire data
}
If it's inconvenient for you, you can avoid using NSURLConnection and use a background thread to grab the data in one piece using:
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://web.service/response.json"]];

ios get page source from web?

I am working on a app and try to display part of a web page.
My idea is first, get page source then parse it!
I already found a useful HTML parser for it but still struggling in how to get page source?
All I found is about UIWebview. Something like:
uiwebview = [[UIWebView loadRequest:[NSURLRequest requestWithURL:url]];
Use
NSString *string = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"] encoding:NSUTF8StringEncoding error:nil];
The disadvantage to using +stringWithContentsOfURL: in the main thread is that your UI will block while the request is active. This becomes especially problematic when the user is on a network with high latency, when the server is slow to respond, or if the request ends up timing out. In the last case, the user may see the UI block for a very long time.
The +stringWithContentsOfURL: method also lacks a way to provide you with error information in the event the server does not return a 200 status.
To perform the request asynchronously without blocking the UI, use NSURLConnection and grab the data in the delegate:
- ( void )connection: (NSURLConnection *)connection didReceiveData: (NSData *)data
{
// receivedData is an NSMutableData object
[ receivedData appendData: data ];
}
And then kick off parsing when the connection finishes:
- ( void )connectionDidFinishLoading: (NSURLConnection *)connection
{
[ self parseHTMLData: receivedData ];
}
The URL Loading System Programming Guide will get you started.

Resources