Pass custom Header for download image in SDWebImage iOS sdk - ios

I need to pass custom HTTPHeader for fetch image but getting error 401. Same token is working for other api.
Header is passed in sdwebimage requesturl:
I am passing custom headers like below code.
SDWebImageDownloader *manager = [SDWebImageManager sharedManager].imageDownloader;
[manager setValue:[DefaultsValues getStringValueFromUserDefaults_ForKey:kTokenKey] forHTTPHeaderField:#"authToken"];
[manager setValue:[DefaultsValues getStringValueFromUserDefaults_ForKey:kUserEmail] forHTTPHeaderField:#"email"];
[manager downloadImageWithURL:[NSURL URLWithString:#"URL STRING"] options:SDWebImageDownloaderUseNSURLCache progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
NSLog(#"%tu",receivedSize);
} completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (error == nil) {
cell.profileImage.image = image;
}
}];

Related

Download image using SDWebImage, error -999 (or) SDWebImageErrorDomain Code=0 "Downloaded image has 0 pixels

I am unable to download the image in Objective c Using SDWebImage 4.4.5 version., I tried the following , nothing worked.
Try 1:
[self.thumbnailImageView sd_setImageWithURL:[NSURL URLWithString:thumbnailURLString] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
}];
Try 2:
[self.thumbnailImageView sd_setImageWithURL:[NSURL URLWithString:thumbnailURLString] placeholderImage:nil options:SDWebImageRefreshCached completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { if(image != nil){
self.thumbnailImageView.image = image;
} else {
NSLog(#"Photo Not Available, fetch");
[self.thumbnailImageView sd_setImageWithURL:[NSURL URLWithString:thumbnailURLString] placeholderImage:nil options:SDWebImageRefreshCached completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if(image != nil) { NSLog(#"Lo Res Image Loaded by %ld from '%#'", (long)cacheType, imageURL);
self.thumbnailImageView.image = image;
} else {
self.thumbnailImageView.image = nil;
} }];
} }];
Try 3:
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:thumbnailURLString] options:SDWebImageDownloaderUseNSURLCache progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
}];
Try 4:
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager loadImageWithURL:[NSURL URLWithString:thumbnailURLString] options:SDWebImageDelayPlaceholder progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
} completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
}];
Try 5:
SDWebImageDownloader *manager = [SDWebImageManager sharedManager].imageDownloader;
[manager setValue:#"application/vnd.learning.events.v1+json" forHTTPHeaderField:#"Accept"];
[manager setValue:#"en-US" forHTTPHeaderField:#"Accept-Language"];
[manager setValue:#"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
//[manager setValue:savedEmail forHTTPHeaderField:#"Email"];
[manager setValue:savedToken forHTTPHeaderField:#"Authorization"];
[manager downloadImageWithURL:[NSURL URLWithString:thumbnailURLString] options:SDWebImageDownloaderHighPriority progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
} completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
self.thumbnailImageView.image = image;
}];
In all the above cases, getting the following error:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://thisisnotcorrecturl.net/api/BAImage?ActionFlagId=97&id=615, NSErrorFailingURLKey=https://thisisnotcorrecturl.net/api/BAImage?ActionFlagId=97&id=615, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <92586538-B07D-46FB-8694-3B29AA3F0CB4>.<7>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <92586538-B07D-46FB-8694-3B29AA3F0CB4>.<7>, NSLocalizedDescription=cancelled}
Earlier, we used v3.5.4 version used, able to download the image with the following code. It's working good.
[self.thumbnailImageView setImageWithURL:[NSURL URLWithString:thumbnailURLString]
placeholderImage:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
if (image != nil){
self.thumbnailImageView.image = [self imageResizingToAspetRatio:image];
}
}];
Kindly let me know if did any mistake, or any suggestions.
Update 1:
Tried to download the image using post man. Kindly check the image.
The same image URL is working on SDWebImage v3.5.4 after i upgraded to SDWebImage v4.4.5, it is not lodging the image.
Is there any configurations am i missing? Installed pod 'SDWebImage/WebP' also, added SD_WEBP=1 in Build settings -> Preprocessor macros.
Error: Error Domain=SDWebImageErrorDomain Code=0 "Downloaded image has 0 pixels" UserInfo={NSLocalizedDescription=Downloaded image has 0 pixels}
http://testingurl.net/api/Image?ActionFlagId=20&id=864
Authorization:
Email: kkk1#ler.com ,
Token: A-BE3D-WER3-847F-ER6YG5678G
Basically, SDWebImage has no issue for downloading them from any URL if the URL contains an image.
But in your case, you have tried to get an image from JSON. Your server API returns a JSON object that contains the image as base64String. When you get the JSON response then you need to get the base64String from json and then create an image from the base64String.
You can use this method for decoding string to image using Objective C.
- (UIImage *)decodeBase64ToImage:(NSString *)strEncodeData {
NSData *data = [[NSData alloc]initWithBase64EncodedString:strEncodeData options:NSDataBase64DecodingIgnoreUnknownCharacters];
return [UIImage imageWithData:data];
}
First, update pod and try SDWebImage pod version 3.8.2.
And the second thing is to check your image URL. because I personally test image download with this pod version and downloading functionality is working.
I faced the same issue and I was trying webP images. I realised later that I need to add webP coder to SDImageCodersManager. Something like
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
I did this in AppDelegate.
If you are suffering such type issue from SDWebImage library you can go with other one as well which are supporting base64 string object
One of best library I found is "Kingfisher"
Hope you like this and it will solve your issue.

Best practice to use AFNetworking

I'm not sure if this is a question with a obvious answer but i haven't been able to find any.
I'm using AFNetworking to connect with my REST server.
I'm doing basic task like uploading and downloading images, posting and getting json etc etc.
What is the best practice to update UI when somethings changes. If for example have successfully downloadet the profile picture and need to change the image inside a tableview.
I only have 1 class that uses AFNetworking my APIConnector
APIConnector.h
#interface APIConnector : NSObject
-(void)downloadClientImageToSystem:(NSString *)imageURL;
#end
APIConnector.m
-(void)downloadClientImageToSystem:(NSString *)imageURL{
//setup
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
//Set url
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#",backendURL,imageURL]];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
//Create a download task
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSString *filename = [NSString stringWithFormat:#"%#.jpeg",[[imageURL componentsSeparatedByString:#"&imgIndex="] lastObject]];
return [documentsDirectoryURL URLByAppendingPathComponent:filename];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
{
if (error) {
NSLog(#"there was an error downloading profile image");
[[NSNotificationCenter defaultCenter] postNotificationName:DLImageFail object:self];
}
else{
NSLog(#"File downloaded to: %#", filePath);
[[NSNotificationCenter defaultCenter] postNotificationName:DLImageSucces object:self];
}
}];
[downloadTask resume];
}
As you can see this currently is using NSNotificationCenter but is this the best solution? I've been reading about Delegates and blocks and it all just seems about loose. Should i implement AFNetworking inside the classes that needs it, like the class where i try to update my tableview?
Thanks :)
Extra code example
-(void)executePostForURL:(NSString *)url dictionary:(NSDictionary *)dict success:(SuccessBlock)success failure:(FailureBlock)failure{
[httpManager POST:url parameters:dict progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
//somehow i need to return [responseObject valueForKey:#"updateLabelString"];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}
I'm trying to call this in viewdidload. This is of course just pseudo code and doesn't work, how do i parse the [responseObject valueForKey#"updateLabelString"] value into my labelToUpdate.text?
-(void)viewDidLoad{
NSDictionary *dicToSendToServer;
UILabel *labelToUpdate = #"temp text";
[apicon executePostForURL:#"serverurl" dictionary:dicToSendToServer success:^(NSString *test){
labelToUpdate.text = test;
}failure:nil];
}
I would declare it like this:
- (void)executePostForURL:(NSString *)url dictionary:(NSDictionary *)dict success:(void (^)(id objectYouRequested))success failure:(void (^)(NSError *error))failure;
I also like to use typedef to avoid some of the block syntax. I typically define the following:
typedef void (^SuccessBlock)(id result);
typedef void (^MySubclassedObjectSuccessBlock)(SubclassedObject *object);
typedef void (^FailureBlock)(NSError *error);
This then simplifies the method declaration above to:
- (void)executePostForURL:(NSString *)url dictionary:(NSDictionary *)dict success:(SuccessBlock)success failure:(FailureBlock)failure;

Make a progress bar for get the response with AFNetworking 3.0

I want to make progress bar for api calling and end with success and i am using the AFNetworking 3.0 version.
I do the following code for measure the progress.
NSURLSessionDataTask *obj = [manager POST:UrlForGetAllCalEntry parameters:jsonDict progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if ([[responseObject valueForKey:#"code"] integerValue] == 200)
{
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[TRAVALARMMANAGER setMessage:error.localizedDescription withView:[APPDELEGATE window] textColor:txtMsgColor bgColor:bgMsgColor];
NSLog(#"Error: %#", error);
}];
[manager setDataTaskDidReceiveDataBlock:^(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSData * _Nonnull data) {
if (dataTask.countOfBytesExpectedToReceive == NSURLSessionTransferSizeUnknown)
return;
if (dataTask != obj)
return;
NSUInteger code = [(NSHTTPURLResponse *)dataTask.response statusCode];
if (!(code> 199 && code < 400))
return;
long long bytesReceived = [dataTask countOfBytesReceived];
long long bytesTotal = [dataTask countOfBytesExpectedToReceive];
NSLog(#"... %lld/%lld",
bytesReceived,
bytesTotal);
}];
But method return from
if (dataTask.countOfBytesExpectedToReceive == NSURLSessionTransferSizeUnknown)
return;
This statement always return true. I don't understand why? . I also print the header and it has "contact length" option.
From the Apple Docs
Discussion
This value is determined based on the Content-Length header received from the server. If that header is absent, the value is NSURLSessionTransferSizeUnknown.
Have you tried to avoid that if statement?
When I download files I don't use that check, I only calculate the progress with the division you did.
hmm, i use this method to monitor the download progress and it works fine for me.
- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
self.downloadTaskDidWriteData = block;
}
↓↓↓↓↓↓ For Example ↓↓↓↓↓↓
Start Download:
NSURL *downloadURL = [NSURL URLWithString:#"example.com/file.mp4"];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
self.downloadTask = [self.manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
// this progress param is "downloadTask operation" progress, it's not the data receiving progress
return nil;
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
}];
[self.downloadTask resume];
Calculate Download Progress:
__weak typeof(self) vc = self;
[self.manager setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
dispatch_async(dispatch_get_main_queue(), ^{
// here is what you want
vc.progressView.progress = (CGFloat)totalBytesWritten / totalBytesExpectedToWrite;
});
}];
Result:
TestProject Image
hope it helps.
I hope it help you, you can have a lot of indicator types
https://github.com/jdg/MBProgressHUD

AFNetworking 2.2.1 loading an image from Amazon S3 server

I'm running into a problem trying to download an image on an Amazon S3 server.
I get the following error:
Error Domain=AFNetworkingErrorDomain Code=-1016 "Request failed: unacceptable content-type: binary/octet-stream"
Anyone has an idea?
This error is generated by
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
method of AFHTTPResponseSerializer in case of unexpectable MIME type of response.
You can fix it by adding required MIME type to response serializer
// In this sample self is inherited from AFHTTPSessionManager
self.responseSerializer = [AFImageResponseSerializer serializer];
NSSet *set = self.responseSerializer.acceptableContentTypes;
self.responseSerializer.acceptableContentTypes = [set setByAddingObject:#"binary/octet-stream"];
Or you can modify AFImageResponseSerializer :
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:#"image/tiff", #"image/jpeg", #"image/gif", #"image/png", #"image/ico", #"image/x-icon", #"image/bmp", #"image/x-bmp", #"image/x-xbitmap", #"image/x-win-bitmap", #"binary/octet-stream", nil];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
self.imageScale = [[UIScreen mainScreen] scale];
self.automaticallyInflatesResponseImage = YES;
#endif
return self;
}
But root of the problem is probably that you save your images to Amazon with wrong MIME type or without type at all. In my code I save images to Amazon with following code
S3PutObjectRequest *putObjectRequest = [ [ S3PutObjectRequest alloc ] initWithKey:keyImage inBucket:self.s3BucketName ];
putObjectRequest.contentType = #"image/jpeg";
putObjectRequest.data = UIImageJPEGRepresentation( [ image fixOrientation ], 0.5f );
putObjectRequest.cannedACL = [ S3CannedACL publicRead ];
Updated for Boto 3
You can check what MIME type it is through your S3 web interface. First select the file you are having problems with, then select the Properties view. In this view open the Metadata section.
If your Content-Type is binary/octet-stream it is unset. Setting it in Boto 3, is different than the above answer for Boto 2. Here is how I do it:
filename = "/home/me/image42.jpeg" #the file I want to upload
bucketname = "myNewBucket" #name of my S3 bucket
key = "myImages/image42.jpeg" #desired name in S3 bucket
s3.Object(bucketname, key).put(Body=open(filename, 'rb'), ACL='public-read',ContentType='image/jpeg')
AFNetworking 3.1.0
You should just modify the acceptableContentTypes property on an existing image response serializer. You can use AFHTTPSessionManager do it.
NSURL *url = [NSURL URLWithString:#"http://..."];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFHTTPResponseSerializer *serializer = [AFHTTPResponseSerializer serializer];
serializer.acceptableContentTypes = [NSSet setWithObject:#"binary/octet-stream"];
manager.responseSerializer = serializer;
Then you can use manager to get the content of URL:
[manager GET:[url absoluteString] parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// ...
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// ...
}];
or use setImageWithURLRequest:placeholderImage:success:^failure:^ for get image:
UIImageView *imageView = [UIImageView new];
[[UIImageView sharedImageDownloader] setSessionManager:manager];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://..."]];
[imageView setImageWithURLRequest:urlRequest placeholderImage:[UIImage new] success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull image) {
// but image can be NSData instead of UIImage
} failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
// ...
}];
But the response can be NSData instead of UIImage.
I hope that it helps you.

Saved image data to core data but launches simulator again, it's gone

I have saved image data to core data, the key is binary data format. After I saved to the core data, I displaying the image in a UIImageView using valueForKey: . But After I launch the simulator again, everything is still saved, but the image data is gone, when I NSLog the key's data description, it's null.
NSDictionary *show = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:nil];
Summary *summary = [NSEntityDescription insertNewObjectForEntityForName:#"Summary" inManagedObjectContext:weakSelf.managedObjectContext];
summary.title = show[#"title"];
summary.poster = show[#"images"][#"poster"];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:summary.poster] options:SDWebImageProgressiveDownload progress:^(NSUInteger receivedSize, long long expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) {
NSData *data = UIImageJPEGRepresentation(image, 0.5);
summary.posterImageData = data;
}];
[weakSelf.managedObjectContext dct_saveWithCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
} else {
NSLog(#"%#", error);
}
}];
I think your current code, the downloadWithURL's completed block is being called after dct_saveWithCompletionHandler , try:
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:summary.poster] options:SDWebImageProgressiveDownload progress:^(NSUInteger receivedSize, long long expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) {
NSData *data = UIImageJPEGRepresentation(image, 0.5);
summary.posterImageData = data;
[weakSelf.managedObjectContext dct_saveWithCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
} else {
NSLog(#"%#", error);
}
}];
}];
Make sure that you're actually saving the managed object context. Doublecheck that you've set the concurrency type for your MOCs correctly, too.

Resources