Getting data from a service Objective-C - ios

am new to iOS, Getting issue with displaying data from below service data
[{
"Name": Rahul,
"FatherName": Ravinder,
"Designation": Engineering,
"Profession": Software Eng,
"Height": "5 ft 3 in",
"Weight": "134.5 lbs"
}]
below is the code what i have tried. Please help me to find the issue. Thanks In Advance.
NameDetails.m
---------------
- (void)viewDidLoad {
[super viewDidLoad];
[self callService:[appDelegate.signUpdata objectForKey:#"id"]];
}
-(void)callService:(NSString *)userid
{
[Utility showIndicator:nil view1:self.view];
JsonServicePostData = [[JsonServiceCls alloc] init];
JsonServicePostData.delegate = self;
[JsonServicePostData Getdata:userid];
}
-(void)DidFinishWebServicesPostData
{
[Utility hideIndicator];
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
_txtName.text=[dict objectForKey:#"Name"];
_txtFName.text=[dict objectForKey:#"FatherName"];
_txtDesg.text=[dict objectForKey:#"Designation"];
_txtprof.text=[dict objectForKey:#"Profession"];
_txtHeight.text=[dict objectForKey:#"Height"];
_txtWeight.text=[dict objectForKey:#"Weight"];
}
}

+(void)makeHttpGETresponceParsingwithSerVer:(NSString *)strServer withCallBack:(void(^)(NSDictionary *dicArr,NSError *error))handler
{
NSURL *urlServer = [NSURL URLWithString:strServer];
NSURLRequest *request = [NSURLRequest requestWithURL:urlServer];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
handler(res,error);
}];
[postDataTask resume];
}
then call your method In viewDidLoad...
[RestClient makeHttpGETresponceParsingwithSerVer:#"YOUR_URL" withCallBack:^(NSDictionary *responceDic, NSError *error) {
_txtName.text =[responceDic objectForKey:#"Name"];
_txtFName.text =[responceDic objectForKey:#"FatherName"];
_txtDesg.text =[responceDic objectForKey:#"Designation"];
_txtprof.text =[responceDic objectForKey:#"Profession"];
_txtHeight.text =[responceDic objectForKey:#"Height"];
_txtWeight.text =[responceDic objectForKey:#"Weight"];
}];
// RestClient is the class name as it is a class method, You can use instance method.

Hi The better approach is for this kind of API call activity you have to go with AFNetworking - https://github.com/AFNetworking/AFNetworking
Its Pretty simple and more powerful. Once you get the json response you have to go for Model Approach.
#import <UIKit/UIKit.h>
#interface NameDetails : NSObject
#property (nonatomic, strong) NSString * designation;
#property (nonatomic, strong) NSString * fatherName;
#property (nonatomic, strong) NSString * height;
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSString * profession;
#property (nonatomic, strong) NSString * weight;
-(instancetype)initWithDictionary:(NSDictionary *)dictionary;
-(NSDictionary *)toDictionary;
#end
#import "RootClass.h"
NSString *const kRootClassDesignation = #"Designation";
NSString *const kRootClassFatherName = #"FatherName";
NSString *const kRootClassHeight = #"Height";
NSString *const kRootClassName = #"Name";
NSString *const kRootClassProfession = #"Profession";
NSString *const kRootClassWeight = #"Weight";
#interface RootClass ()
#end
#implementation RootClass
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
-(instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if(![dictionary[kRootClassDesignation] isKindOfClass:[NSNull class]]){
self.designation = dictionary[kRootClassDesignation];
}
if(![dictionary[kRootClassFatherName] isKindOfClass:[NSNull class]]){
self.fatherName = dictionary[kRootClassFatherName];
}
if(![dictionary[kRootClassHeight] isKindOfClass:[NSNull class]]){
self.height = dictionary[kRootClassHeight];
}
if(![dictionary[kRootClassName] isKindOfClass:[NSNull class]]){
self.name = dictionary[kRootClassName];
}
if(![dictionary[kRootClassProfession] isKindOfClass:[NSNull class]]){
self.profession = dictionary[kRootClassProfession];
}
if(![dictionary[kRootClassWeight] isKindOfClass:[NSNull class]]){
self.weight = dictionary[kRootClassWeight];
}
return self;
}
/**
* Returns all the available property values in the form of NSDictionary object where the key is the approperiate json key and the value is the value of the corresponding property
*/
-(NSDictionary *)toDictionary
{
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if(self.designation != nil){
dictionary[kRootClassDesignation] = self.designation;
}
if(self.fatherName != nil){
dictionary[kRootClassFatherName] = self.fatherName;
}
if(self.height != nil){
dictionary[kRootClassHeight] = self.height;
}
if(self.name != nil){
dictionary[kRootClassName] = self.name;
}
if(self.profession != nil){
dictionary[kRootClassProfession] = self.profession;
}
if(self.weight != nil){
dictionary[kRootClassWeight] = self.weight;
}
return dictionary;
}
The above one is Model Class.
Your JSON look like a array. So you need to iterate the Dictionary values on it. Other than that you may pass it directly.
Now in your ViewController class initiate the mutable array
and pass the response like
NSArray *arrayData = ResponseFromAFNETWORKING
for (NSDictionary *data in arrayData) {
NameDetails *modelFeed = [[NameDetails alloc] initFromDictinary:data]
[self.YourMutableDictionary addObject:modelFeed]
}
self.updateDisplay:self.YourMutableDictionary[0] // If not array No iteration, you can prepare the model and pass it directly
----------------------------------------
- (void)updateDisplay:(NameDetails *)feed {
_txtName.text =feed.Name;
_txtFName.text =feed.FatherName;
_txtDesg.text =feed.Designation;
_txtprof.text =feed.Profession;
_txtHeight.text =feed.Height;
_txtWeight.text =feed.Weight;
}
Hope this will help. This is a robust and elastic approach, thread safe mechanism too

Related

Unarchive custom object with secure coding in iOS

I'm encoding an array of objects that conform to NSSecureCoding successfully like this.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
How do I unarchive this data? I'm trying like this but it's returning nil.
NSError *error = nil;
NSArray<MyClass *> *array = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];
This is the error it returns.
The data couldn’t be read because it isn’t in the correct format.
Here is a simple example
#interface Secure:NSObject<NSSecureCoding> {
NSString* name;
}
#property NSString* name;
#end
#implementation Secure
#synthesize name;
// Confirms to NSSecureCoding
+ (BOOL)supportsSecureCoding {
return true;
}
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
[coder encodeObject:self.name forKey: #"name"];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
self = [self init];
if (self){
self.name = [coder decodeObjectOfClass:[NSString class] forKey:#"name"];
}
return self;
}
#end
Usage
Secure *archive = [[Secure alloc] init];
archive.name = #"ARC";
NSArray* array = #[archive];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
NSArray<Secure *> *unarchive = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSObject class] fromData:data error:nil];
NSLog(#"%#", unarchive.firstObject.name);

Multiple NSObject Initialization Causes code to stop executing

So I have a custom TVShow object that has some base fields like id, showName, airDate etc. which are all either NSStrings or NSIntegers and I am attempting to create a bunch of these objects via some data I have gotten from an API online.
So I loop through my NSArray of JSON data and create a TVShow object for each response:
TVShow *show = [[TVShow alloc] initWithData:[NSJSONSerialization JSONObjectWithData:data options:0 error:&error]];
[self.showArray addObject:show];
However, only 7 of these ever get created and then any code below this just ceases to run. I have a NSLog(#"Added"); printing after I create the show and it only gets called 6 times. If I add breakpoints after any of this code, they never get called. I'm not sure what's going on but it must be something to do with how I have set up my TVShow object?
It currently looks like:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface TVShow : NSObject
#property NSInteger showID;
#property NSString *showName;
#property NSString *airDate;
#property double rating;
#property NSString *imageUrl;
#property NSString *showSummary;
#property NSString *episodeSummary;
#property NSInteger season;
#property NSInteger episode;
- (id)initWithData:(NSDictionary *)data;
#end
and the .m file:
#import <UIKit/UIKit.h>
#import "TVShow.h"
#implementation TVShow
- (id)initWithData:(NSDictionary*)data {
self = [super init];
if(self) {
[self buildObjectFromData:data];
}
return self;
}
-(void)buildObjectFromData:(NSDictionary*)data {
NSDictionary *dict = [data objectForKey:#"_embedded"];
NSDictionary *dict2 = [dict objectForKey:#"nextepisode"];
NSDictionary *dict3 = [data objectForKey:#"image"];
NSString *airDate = [dict2 valueForKey:#"airstamp"];
NSInteger season = [[dict2 valueForKey:#"season"] integerValue];
NSInteger episode = [[dict2 valueForKey:#"episode"] integerValue];
NSString *episodeSummary = [dict2 valueForKey:#"summary"];
NSString *showName = [data valueForKey:#"name"];
NSString *showSummary = [data valueForKey:#"summary"];
NSString *imageUrl = [dict3 valueForKey:#"medium"];
NSInteger showID = [[data valueForKey:#"id"] integerValue];
self.airDate = airDate;
self.showName = showName;
self.season = season;
self.episode = episode;
self.showSummary = [self stringByStrippingHTML:showSummary];
self.episodeSummary = [self stringByStrippingHTML:episodeSummary];
self.imageUrl = imageUrl;
self.showID = showID;
}
-(NSString *) stringByStrippingHTML:(NSString*)string {
NSRange r;
NSString *s = string;
while ((r = [s rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
s = [s stringByReplacingCharactersInRange:r withString:#""];
return s;
}
#end
If I create the object as just: [[TVShow alloc] init]; everything works fine, so it must be something wrong with this model is what I'm thinking. I'm unsure of what to try next, but any help would be greatly appreciated here.
Turns out there were cases that casued:
-(NSString *) stringByStrippingHTML:(NSString*)string {
NSRange r;
NSString *s = string;
while ((r = [s rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
s = [s stringByReplacingCharactersInRange:r withString:#""];
return s;
}
to go into infinite loops. Removing this snippet fixed the freeze.

AFNetworking 2.5.3 Serialization of array to send indexes explicitely

I use
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
and this sends out the call to the server when having an array in the format:
payments[]
payments[][email]=0&
payments[][category]=&
payments[][email]=1&
payments[][category]=&
I need the indexes for the server like.
payments[]
payments[0][email]=0&
payments[0][category]=&
payments[1][email]=1&
payments[1][category]=&
How can I achieve that?
Thanks.
AFHTTPRequestSerializer has a method setQueryStringSerializationWithBlock which allows you to provide your own block for serializing the parameters.
Unfortunately, the internal AF* functions for serialization are private, but you can copy them and make a small modification in these lines to add the indexes.
To set your own serialization block:
[serializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, id parameters, NSError *__autoreleasing *error) {
return ZAFQueryStringFromParametersWithEncoding(parameters, serializer.stringEncoding);
}];
Including the code, where the AFNetworking functions have been copied and prefixed with a Z to ZAF:
static NSString * const kAFCharactersToBeEscapedInQueryString = #":/?&=;+!##$()',*";
static NSString * AFPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
static NSString * const kAFCharactersToLeaveUnescapedInQueryStringPairKey = #"[].";
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
}
static NSString * AFPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
}
#pragma mark -
#interface ZAFQueryStringPair : NSObject
#property (readwrite, nonatomic, strong) id field;
#property (readwrite, nonatomic, strong) id value;
- (id)initWithField:(id)field value:(id)value;
- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding;
#end
#implementation ZAFQueryStringPair
- (id)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding);
} else {
return [NSString stringWithFormat:#"%#=%#", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], stringEncoding)];
}
}
#end
#pragma mark -
static NSArray * ZAFQueryStringPairsFromDictionary(NSDictionary *dictionary);
static NSArray * ZAFQueryStringPairsFromKeyAndValue(NSString *key, id value);
static NSString * ZAFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (ZAFQueryStringPair *pair in ZAFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]];
}
return [mutablePairs componentsJoinedByString:#"&"];
}
NSArray * ZAFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return ZAFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
NSArray * ZAFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"description" ascending:YES selector:#selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:#[ sortDescriptor ]]) {
id nestedValue = [dictionary objectForKey:nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:ZAFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:#"%#[%#]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
NSInteger idx = 0;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:ZAFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:#"%#[%ld]", key, idx++], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:#[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:ZAFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[ZAFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}

SDWebImage displaying wrong images in UITableView

In my iOS app, I'm displaying images inside multiple UITableViewCells. However, it's not displaying the correct images in each cell.
First I load some content from a Feedly stream with the method below:
- (void)loadStreams {
NSString *feedName = [NSString stringWithFormat:#"%#-id", self.category];
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *accessToken = [standardUserDefaults objectForKey:#"AccessToken"];
NSString *feedId = [standardUserDefaults objectForKey:feedName];
NSString *feedPartial = [feedId stringByReplacingOccurrencesOfString:#"/" withString:#"%2F"];
NSString *feedUrl = [NSString stringWithFormat:#"https://sandbox.feedly.com/v3/streams/%#/contents", feedPartial];
NSLog(#"The Feedly url is: %#", feedUrl);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:feedUrl]];
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[mutableRequest addValue:accessToken forHTTPHeaderField:#"Authorization"];
request = [mutableRequest copy];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *jsonArray = (NSArray *)[responseObject objectForKey:#"items"];
self.continuation = [responseObject objectForKey:#"continuation"];
NSMutableArray *tempStreams = [[NSMutableArray alloc] init];
for (NSDictionary *dic in jsonArray) {
NSLog(#"Dic contains: %#", dic);
NSDictionary *originArray = [dic objectForKey:#"origin"];
NSDictionary *visualArray = [dic objectForKey:#"visual"];
NSArray *alternateArray = [dic objectForKey:#"alternate"];
NSDictionary *alternate = [alternateArray objectAtIndex:0];
NSString *image = [visualArray objectForKey:#"url"];
NSString *title = [dic objectForKey:#"title"];
NSString *author = [dic objectForKey:#"author"];
NSString *date = [dic objectForKey:#"published"];
NSDictionary *contentum = [dic objectForKey:#"content"];
NSString *content = [contentum objectForKey:#"content"];
NSString *owner = [originArray objectForKey:#"title"];
NSString *givenid = [dic objectForKey:#"id"];
NSString *href = [alternate objectForKey:#"href"];
NSDate *publisher = [NSDate dateWithTimeIntervalSince1970:([date doubleValue] / 1000.0)];
NSString *published = publisher.timeAgoSinceNow;
NSDictionary *data = [[NSDictionary alloc] initWithObjectsAndKeys:title, #"title", image, #"imageurl", published, #"published", owner, #"owner", content, #"content", givenid, #"givenid", href, #"href", author, #"author", nil];
Stream *stream = [[Stream alloc] initWithDictionary:data];
[tempStreams addObject:stream];
}
self.streams = [[NSMutableArray alloc] initWithArray:tempStreams];
tempStreams = nil;
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Services"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}];
[operation start];
}
This passes the data to an object called Stream, which consists of the code below:
Stream.h
#import <Foundation/Foundation.h>
#interface Stream : NSObject
#property (strong, nonatomic)NSString *name;
#property (strong, nonatomic)NSString *thumbnail;
#property (strong, nonatomic)NSString *photo;
#property (strong, nonatomic)NSString *published;
#property (strong, nonatomic)NSString *content;
#property (strong, nonatomic)NSString *givenid;
#property (strong, nonatomic)NSString *linky;
#property (strong, nonatomic)NSString *author;
- (id)initWithName:(NSString *)aName
thumbnail:(NSString *)aThumbnail
photo:(NSString *)aPhoto
published:(NSString *)aPublished
content:(NSString *)aContent
givenid:(NSString *)aId
linky:(NSString *)aLinky
author:(NSString *)aAuthor;
- (id)initWithDictionary:(NSDictionary *)dic;
#end
Stream.m
#import "Stream.h"
#implementation Stream
//The designed initializer
- (id)initWithName:(NSString *)aName
thumbnail:(NSString *)aThumbnail
photo:(NSString *)aPhoto
published:(NSString *)aPublished
content:(NSString *)aContent
givenid:(NSString *)aId
linky:(NSString *)aLinky
author:(NSString *)aAuthor{
self = [super init];
if (self) {
self.name = aName;
self.thumbnail = aThumbnail;
self.photo = aPhoto;
self.published = aPublished;
self.content = aContent;
self.givenid = aId;
self.linky = aLinky;
self.author = aAuthor;
}
return self;
}
- (id)initWithDictionary:(NSDictionary *)dic {
self = [self initWithName:dic[#"title"] thumbnail:dic[#"imageurl"] photo:dic[#"imageurl"] published:dic[#"published"] content:dic[#"content"] givenid:dic[#"givenid"] linky:dic[#"href"] author:dic[#"author"]];
return self;
}
- (id)init {
self = [self initWithName:#"Undifined" thumbnail:#"Undifined" photo:#"Undifined" published:#"Undifined" content:#"Undifined" givenid:#"Undifined" linky:#"Undifined" author:#"Undifined"];
return self;
}
#end
And in the end I build a cell like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * reuseIdentifier = #"programmaticCell";
MGSwipeTableCell * cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}
CGFloat brightness = [UIScreen mainScreen].brightness;
cell.textLabel.text = [self.streams[indexPath.row] name];
cell.detailTextLabel.text = [self.streams[indexPath.row] published];
NSString *imageUrl = [NSString stringWithFormat: #"%#", [self.streams[indexPath.row] photo]];
NSLog(#"Image is: %# and path is: %d", imageUrl, indexPath.row);
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:imageUrl]
placeholderImage:[UIImage imageNamed:#"tile-blue.png"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];
cell.delegate = self; //optional
return cell;
}
What happens though, is that it displays the wrong image in a lot of cells and sometimes the same image for a couple of cells. What am I doing wrong here?
These are symptoms of cell reuse. There are two issues you will have to deal with.
(1) you should reset your cell's content before it is reused. To do this you can override prepareForReuse in the cell and nil out the relevant properties (such as cell.imageView). If you don't do this, you will see the old image -after- the cell has been recycled, before SDWebImage has assigned a new image.
(2) as SDWebImage image retrieval is async, the image may arrive after the cell has scrolled off the screen (and recycled with new content. You need to check whether the image is still relevant before assigning it to the imageView. I am not sure if this is possible with the SDWebImage UIImageView category method. You may have to dissect SDWebImage a little . You can get more control over the process using the SDWebImageManager method:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;
You could use it something like this (in CellForRowAtIndexPath)
[[SDWebImageManager defaultManager] downloadImageWithURL:url
options:0
progress:nil
completed:
^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
//image is still valid for this cell
cell.image = image;
}
}];
Push a unique id on the stack before your closure and check it when your closure completes
prepareForReuse
Like this:
func updateArtistImage(url: URL) {
let _eventId = self.event?.id
SDWebImageManager.shared().loadImage(with: url, options: [], progress: nil) { (image, data, error, cacheType, finished, url) in
if self.event!.id == _eventId {
if error == nil {
self.artistImageView.image = image
} else {
self.artistImageView.image = UIImage(named: "error_image")
}
}
}
}
and this:
override func prepareForReuse() {
super.prepareForReuse()
self.artistImageView.image = nil
}

How to convert NSDictionary to custom object

I have a json object:
#interface Order : NSObject
#property (nonatomic, retain) NSString *OrderId;
#property (nonatomic, retain) NSString *Title;
#property (nonatomic, retain) NSString *Weight;
- (NSMutableDictionary *)toNSDictionary;
...
- (NSMutableDictionary *)toNSDictionary
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setValue:self.OrderId forKey:#"OrderId"];
[dictionary setValue:self.Title forKey:#"Title"];
[dictionary setValue:self.Weight forKey:#"Weight"];
return dictionary;
}
In string this is:
{
"Title" : "test",
"Weight" : "32",
"OrderId" : "55"
}
I get string JSON with code:
NSMutableDictionary* str = [o toNSDictionary];
NSError *writeError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:str options:NSJSONWritingPrettyPrinted error:&writeError];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
Now I need to create and map object from JSON string:
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:nil error:&e];
This returns me filled NSDictionary.
What should I do to get object from this dictionary?
Add a new initWithDictionary: method to Order:
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
if (self = [super init]) {
self.OrderId = dictionary[#"OrderId"];
self.Title = dictionary[#"Title"];
self.Weight = dictionary[#"Weight"];
}
return self;
}
Don't forget to add initWithDictionary's signature to Order.h file
In the method where you get JSON:
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:nil error:&e];
Order *order = [[Order alloc] initWithDictionary:dict];
If the property names on your object match the keys in the JSON string you can do the following:
To map the JSON string to your Object you need to convert the string into a NSDictionary first and then you can use a method on NSObject that uses Key-Value Coding to set each property.
NSError *error = nil;
NSData *jsonData = ...; // e.g. [myJSONString dataUsingEncoding:NSUTF8Encoding];
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingOptionsAllowFragments error:&error];
MyObject *object = [[MyObject alloc] init];
[object setValuesForKeysWithDictionary:jsonDictionary];
If the keys do not match you can override the instance method of NSObject -[NSObject valueForUndefinedKey:] in your object class.
To map you Object to JSON you can use the Objective-C runtime to do it automatically. The following works with any NSObject subclass:
#import <objc/runtime.h>
- (NSDictionary *)dictionaryValue
{
NSMutableArray *propertyKeys = [NSMutableArray array];
Class currentClass = self.class;
while ([currentClass superclass]) { // avoid printing NSObject's attributes
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(currentClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if (propName) {
NSString *propertyName = [NSString stringWithUTF8String:propName];
[propertyKeys addObject:propertyName];
}
}
free(properties);
currentClass = [currentClass superclass];
}
return [self dictionaryWithValuesForKeys:propertyKeys];
}
Assuming that your properties names and the dictionary keys are the same, you can use this function to convert any object
- (void) setObject:(id) object ValuesFromDictionary:(NSDictionary *) dictionary
{
for (NSString *fieldName in dictionary) {
[object setValue:[dictionary objectForKey:fieldName] forKey:fieldName];
}
}
this will be more convenient for you :
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dictionary];}
return self;
}
The perfect way to do this is by using a library for serialization/deserialization
many libraries are available but one i like is
JagPropertyConverter
https://github.com/jagill/JAGPropertyConverter
it can convert your Custom object into NSDictionary and vice versa
even it support to convert dictionary or array or any custom object within your object (i.e Composition)
JAGPropertyConverter *converter = [[JAGPropertyConverter alloc]init];
converter.classesToConvert = [NSSet setWithObjects:[Order class], nil];
#interface Order : NSObject
#property (nonatomic, retain) NSString *OrderId;
#property (nonatomic, retain) NSString *Title;
#property (nonatomic, retain) NSString *Weight;
#end
//For Dictionary to Object (AS IN YOUR CASE)
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setValue:self.OrderId forKey:#"OrderId"];
[dictionary setValue:self.Title forKey:#"Title"];
[dictionary setValue:self.Weight forKey:#"Weight"];
Order *order = [[Order alloc]init];
[converter setPropertiesOf:order fromDictionary:dictionary];
//For Object to Dictionary
Order *order = [[Order alloc]init];
order.OrderId = #"10";
order.Title = #"Title;
order.Weight = #"Weight";
NSDictionary *dictPerson = [converter convertToDictionary:person];
Define your custom class inherits from "AutoBindObject". Declare properties which has the same name with keys in NSDictionary. Then call method:
[customObject loadFromDictionary:dic];
Actually, we can customize class to map different property names to keys in dictionary. Beside that, we can bind nested objects.
Please have a look to this demo. The usage is easy:
https://github.com/caohuuloc/AutoBindObject

Resources