Replace smiles with images in NSAttributedString - ios

I have a list of Emoji entities and each of the has property codes and I want to check string ("]:-)") if it contains any of them and then replace smile with an image.
for (Emoji *emoji in self.emojis) {
for (NSString *code in emoji.codes) {
NSString *pattern = [NSRegularExpression escapedPatternForString:code];
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
NSArray *matches = [regex matchesInString:[sourceString string] options:0 range:NSMakeRange(0, [sourceString length])];
[matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull aResult, NSUInteger idx, BOOL * _Nonnull stop) {
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
[attachment setImage:[UIImage imageNamed:emoji.image]];
NSAttributedString *replacement = [NSAttributedString attributedStringWithAttachment:attachment];
[sourceString replaceCharactersInRange:[aResult range] withAttributedString:replacement];
}];
}
}
The problem is that smile with code ]:-) contains :-) and my method replacing it with next: bracket ] + [image] for :-), it is because :-) goes first in list.
How can I check for exact string?
I've tried:
]:-/), \\b]:-/)\\b, /^]:-/)$/
Maybe there is better solution to make this working.

If I understood correctly your current structure:
#interface Emoji : NSObject
#property (nonatomic, strong) NSString *image; //I'd expect a UIImage there and not an image name
#property (nonatomic, strong) NSArray *codes;
#end
A possible solution is instead to use a single couple of values: imageName/code
#interface Emoji : NSObject
#property (nonatomic, strong) NSString *image; //I'd expect a UIImage there and not an image name
#property (nonatomic, strong) NSString *code;
#end
self.emojis will have plenty of Emoji object that may have the same image name for different code, but an advantage of doing that is this little trick:
Sort self.emojis in a way that "smaller" emojis are at the end. So you'll replace first only the "lengthy" ones and the the smaller ones.
self.emojis = [arrayOfSingleEmojis sortedArrayUsingComparator:^NSComparisonResult(Emoji * _Nonnull emoji1, Emoji * _Nonnull emoji2) {
NSUInteger length1 = [[emoji1 code] length];
NSUInteger length2 = [[emoji2 code] length];
return [#(length2) compare:#(length1)]; //Or reverse length1 & length2, I never know, I always have to test, but I think it's the correct one
}];
So in your current case: ]:-) will be replace before :-) so you should have <imageFor:">:-)"] instead of ]<imageFor:":-)>

Related

create loop for array to get string from json String?

i need to display a Table View containing information from web service response i do no where iam doing wrong here my sample code
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
recordResults =NO;
appDelegate.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
appDelegate.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
appDelegate.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
appDelegate.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
}
i need to parse 4 values from plan_history like "rate","description","validity","plan type"
when i run my app i getting only one set of value in Table view . i.e my json string contains more than 20 records containing rate,description,validity and plan type
can u show me how to loop my json value and display all my records in Table View
You should eliminate those calls to allValues and valueForKey, as repeatedly calling those methods is very inefficient ways to tackle JSON parsing.
In one of your comments, you said that your JSON looked like:
{
"plan_history": [
{
"rate": "₹1000",
"description": "FullTalktimeTopupRs.1000FullTalktime",
"validity": "Validity: 0\r",
"plantype": "FullTalkTime"
},
{
"rate": "₹508",
"description": "FullTalktimeTopupRs.558morethanFullTalktime",
"validity": "Validity: 2\r",
"plantype": "FullTalkTime"
}
]
}
(I wonder if there was something before this plan_history entry given your allValues reference, but unless you tell us otherwise, I'll assume this is what the original JSON looked like.)
If so, to parse it you would do:
NSMutableArray *results = [NSMutableArray array];
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *planHistory = json[#"plan_history"];
for (NSDictionary *planHistoryEntry in planHistory) {
NSString *rateString = planHistoryEntry[#"rate"];
NSString *description = planHistoryEntry[#"description"];
NSString *validity = planHistoryEntry[#"validity"];
NSString *planType = planHistoryEntry[#"plantype"];
// now do whatever you want with these four values.
// for example, I'd generally create a custom object I defined elsewhere for these four values and add to results, e.g.
[results addObject:[PlanHistoryEntry planHistoryEntryWithRate:rateString
description:description
validity:validity
planType:planType]];
}
// now do something with results, e.g. store it in some property in `appDelegate`, etc.
Where, PlanHistoryEntry might be defined like so:
#interface PlanHistoryEntry : NSObject
#property (nonatomic, copy) NSString *rateString;
#property (nonatomic, copy) NSString *planDescription; // note, do not use `description` for property name
#property (nonatomic, copy) NSString *validity;
#property (nonatomic, copy) NSString *planType;
+ (instancetype) planHistoryEntryWithRate:(NSString *)rateString
planDescription:(NSString *)planDescription
validity:(NSString *)validity
planType:(NSString *)planType;
#end
#implementation PlanHistoryEntry
+ (instancetype) planHistoryEntryWithRate:(NSString *)rateString
planDescription:(NSString *)planDescription
validity:(NSString *)validity
planType:(NSString *)planType
{
PlanHistoryEntry *entry = [[self alloc] init];
entry.rateString = rateString;
entry.planDescription = planDescription;
entry.validity = validity;
entry.planType = planType;
return entry;
}
#end
But I don't want you to get lost in the minutiae of this answer (because given the ambiguity of the question, I may have gotten some details wrong). The key point is that you should not be using allValues or valueForKey. Just navigate the JSON structure more directly as illustrated above.
Try this,
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *rateArray = [[json objectForKey:#"plan_history"] objectForKey:#"rate"];
NSArray * descriptionArray = [[json objectForKey:#"plan_history"] objectForKey:#"description"];
NSArray * validityArray = [[json objectForKey:#"plan_history"] objectForKey:#"validity"];
NSArray * plantypeArray = [[json objectForKey:#"plan_history"] objectForKey:#"plantype"];
and use rateArray, descriptionArray etc.
You can create a class storing your data as follows:
Something like:
planClass.h
#property(nonatomic, strong) NSString * rateString;
#property(nonatomic, strong) NSString * descriptionString;
#property(nonatomic, strong) NSString * validityString;
#property(nonatomic, strong) NSString * plantypeString;
plan.m
//#synthesize the properties of .h
Now in your .m file where you want to parse the data you can do something like:
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
planClass *pc = [[planClass alloc]init];
recordResults =NO;
pc.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
pc.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
pc.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
pc.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
[appDelegate.arrayPlan addObject:pc];
}
NSLog(#"appDelegate.arrayPlan >> %#",appDelegate.arrayPlan); // you'll get array of planClass objects
You can now access the arrayPlan declared in appDelegate as follows:
for(id *obj in arrayPlan)
{
planClass *pc = (planClass *)obj;
NSLog("rate: %#",[pc valueForKey:#"rateString"]);
NSLog("descriptionString: %#",[pc valueForKey:#"descriptionString"]);
NSLog("validityString: %#",[pc valueForKey:#"validityString"]);
NSLog("plantypeString: %#",[pc valueForKey:#"plantypeString"]);
}
Hope this helps.
you need to store that value in fatalist control means in NSMutable array like this.
NSData *data = [soapResultsString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSMutableArray *arrayHistory = [[NSMutableArray alloc]init];
NSArray *array = [json allValues];
for (int i=0; i<array.count; i++)
{
recordResults =NO;
appDelegate.rateString =[[[json valueForKey:#"plan_history"]valueForKey:#"rate"]objectAtIndex:i];
appDelegate.descriptionString=[[[json valueForKey:#"plan_history"]valueForKey:#"description"]objectAtIndex:i];
appDelegate.validityString=[[[json valueForKey:#"plan_history"]valueForKey:#"validity"]objectAtIndex:i];
appDelegate.plantypeString=[[[json valueForKey:#"plan_history"]valueForKey:#"plantype"]objectAtIndex:i];
[arrayHistory addObject:appDelegate.rateString];
[arrayHistory addObject:appDelegate.descriptionString];
[arrayHistory addObject:appDelegate.validityString];
[arrayHistory addObject:appDelegate.plantypeString];
}
Now use
arrayHistory
to load data in table view

Adding Custom Objects to NSMutableArray

I mainly program in Java and can't understand why this isn't working. I'm trying to create a temporary object "Judge" in my for loop. I then want to add that object to an NSMutableArray so in the end I have an array filled with different Judge objects. After the for loop I run through all the objects in the Array and they're all the last Judge Object.
The NSLog shows that "JudgeTemp" object is being assigned the right values while in the for loop. My guess is that it's not creating a new object called JudgeTemp every time but referencing the old already created JudgeTemp.
NSMutableArray *Judges = [NSMutableArray arrayWithCapacity:30];
for (int i=0; i<[courtinfoarray count]; i++) {
Judge1= [[courtinfoarray objectAtIndex:i] componentsSeparatedByString:#"|"];
Judge *JudgeTemp=[[Judge alloc]init];
[JudgeTemp setName:[Judge1 objectAtIndex:0] picture:[Judge1 objectAtIndex:1] courtroom:[Judge1 objectAtIndex:2] phone:[Judge1 objectAtIndex:3] undergrad:[Judge1 objectAtIndex:4] lawschool:[Judge1 objectAtIndex:5] opdasa:[Judge1 objectAtIndex:6] career:[Judge1 objectAtIndex:7] judgecode:[Judge1 objectAtIndex:8]];
NSLog(#"%#",[JudgeTemp getName]);
[Judges addObject:JudgeTemp];
NSLog(#"%#",[[Judges objectAtIndex:i]getName]);
}
Judges Class
#implementation Judge
NSString *name;
NSString *picture;
NSString *courtroom;
NSString *phone;
NSString *undergrad;
NSString *lawschool;
NSString *opdasa;
NSString *career;
NSString *judgecommentcode;
-(void) setName:(NSString *)n picture:(NSString *) p courtroom:(NSString *)c phone:(NSString *)ph undergrad: (NSString *) u lawschool: (NSString *)l opdasa: (NSString *) o career: (NSString *)ca judgecode: (NSString *)jcode{
name = n;
picture = p;
courtroom = c;
phone = ph;
undergrad = u;
lawschool = l;
opdasa = o;
career = ca;
judgecommentcode = jcode;
}
-(NSString*) getName{
return name;
}
The problem is with your Judge class. When you define variables directly in your #implementation they have global scope and are not instance variables. What you need to do is put those variable declarations in your #interface instead:
#interface Judge : NSObject {
NSString *name;
NSString *picture;
NSString *courtroom;
NSString *phone;
NSString *undergrad;
NSString *lawschool;
NSString *opdasa;
NSString *career;
NSString *judgecommentcode;
}
// ...
#end
Edit: Apparently you can declare them in your #implementation, you just have to wrap them in { }. See: Instance variables declared in ObjC implementation file

Generate MD5 hash from Objective-C object [duplicate]

This question already has answers here:
MD5 algorithm in Objective-C
(5 answers)
Closed 9 years ago.
I'd like to generate an MD5 hash for an NSObject:
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
What is the best way to do so? I've seen examples for hashing from a dictionary or an array, but not from an entire NSObject.
To generate a MD5 hash for an NSObject or a subclass of NSObject, you need to convert it into something that's easily hashable but still represents the state of the instance. A JSON string is one such option. The code looks like this:
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * type;
#property (nonatomic, retain) NSString * unit;
#property (nonatomic, retain) NSArray * fields;
- (NSString *)md5Hash;
#end
Model.m
#import <CommonCrypto/CommonDigest.h>
#import "Model.h"
#implementation Model
- (NSString *)md5Hash
{
// Serialize this Model instance as a JSON string
NSDictionary *map = #{ #"name": self.name, #"type": self.type,
#"unit": self.unit, #"fields": self.fields };
NSError *error = NULL;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:map
options:NSJSONWritingPrettyPrinted
error:&error];
if (error != nil) {
NSLog(#"Serialization Error: %#", error);
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Now create the MD5 hashs
const char *ptr = [jsonString UTF8String];
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(ptr, strlen(ptr), md5Buffer);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x",md5Buffer[i]];
return output;
}
#end
Then you can easily retrieve the MD5 hash just by calling the md5Hash method
Model *obj = [Model new];
obj.name = #"...";
obj.type = #"...";
obj.unit = #"...";
obj.fields = #[ ... ];
NSString *hashValue = [obj md5Hash];
You can convert the object into a dictionary if you already have code for creating the hash:
NSDictionary *dict = [myObject dictionaryWithValuesForKeys:#[#"name", #"type", #"unit", #"fields"]];
Or you could implement <NSCoding> on your class, archive it and hash the resulting data.

Key-Value Coding Get Element in List

I have two objects:
#interface AObject : NSObject
#property NSArray *bObjects;
#end
#interface BObject : NSObject
#property NSString *name;
#end
Using key-value coding on an instance of AObject, I can get the list of bObjects (#"self.bObjects"), and a list of bObjects' names (#"self.bObjects.name").
However, what I want is the name of only the first of bObjects. My gut is that key-value coding should support list subscripting, like this: #"bObjects[0].name".
But that doesn't seem to exist. How do I get a single entity; the name of an AObject's first BObject, using key-value coding?
Footnote: I realized in my last question I was stupidly conflating NSPredicate and KV-coding.
As Martin R mentioned in the comments, currently the best option would be to create a firstBObject property in the AObject class.
AObject.h/m
#class BObject;
#interface AObject : NSObject
+ (AObject*)aObjectWithBObjects:(NSArray*)bObjects;
#property NSArray *bObjects;
#property (nonatomic, readonly) BObject *firstBObject;
#end
#implementation AObject
+ (AObject*)aObjectWithBObjects:(NSArray*)bObjects
{
AObject *ao = [[self alloc] init];
ao.bObjects = bObjects;
return ao;
}
- (BObject*)firstBObject
{
return [self.bObjects count] > 0 ? [self.bObjects objectAtIndex:0] : nil;
}
#end
BObject.h/m
#interface BObject : NSObject
+ (BObject*)bObjectWithName:(NSString*)name;
#property NSString *name;
#end
#implementation BObject
+ (BObject*)bObjectWithName:(NSString *)name
{
BObject *bo = [[self alloc] init];
bo.name = name;
return bo;
}
#end
Usage:
NSArray *aobjects = #[
[AObject aObjectWithBObjects:#[
[BObject bObjectWithName:#"A1B1"],
[BObject bObjectWithName:#"A1B2"],
[BObject bObjectWithName:#"A1B3"],
[BObject bObjectWithName:#"A1B4"]
]],
[AObject aObjectWithBObjects:#[
[BObject bObjectWithName:#"A2B1"],
[BObject bObjectWithName:#"A2B2"],
[BObject bObjectWithName:#"A2B3"],
[BObject bObjectWithName:#"A2B4"]
]],
[AObject aObjectWithBObjects:#[
[BObject bObjectWithName:#"A3B1"],
[BObject bObjectWithName:#"A3B2"],
[BObject bObjectWithName:#"A3B3"],
[BObject bObjectWithName:#"A3B4"]
]]
];
NSLog(#"%#", [aobjects valueForKeyPath:#"firstBObject.name"]);
Results
(
A1B1,
A2B1,
A3B1 )
So as it turns out, I had the fortune of being able to simply override -valueForKey: in the root class (AObject). It bears repeating that -valueForKeyPath: calls -valueForKey: on every key, which is cool.
Since that might not be applicable to everyone, and this might be too much manipulation of default, expected behavior, this definitely not the "right" answer.
But here it is anyway:
- (id)valueForKey:(NSString *)string
{
if ([string characterAtIndex: [string length] - 1] == ']') // Trying to subscript
{
NSRegularExpression *subscriptRegex = [[NSRegularExpression alloc] initWithPattern: #"([a-zA-Z]+)\\[([0-9]+)\\]"
options: (NSRegularExpressionOptions)0
error: nil];
NSString *key = [subscriptRegex stringByReplacingMatchesInString: string
options: (NSMatchingOptions)0
range: NSMakeRange(0, [string length])
withTemplate: #"$1"];
id valueForKey = [self valueForKey: key];
if (!key || !valueForKey || ![valueForKey respondsToSelector: #selector(objectAtIndexedSubscript:)])
return nil;
NSInteger index = [[subscriptRegex stringByReplacingMatchesInString: string
options: (NSMatchingOptions)0
range: NSMakeRange(0, [string length])
withTemplate: #"$2"] integerValue];
if ((index < 0) || (index >= [valueForKey count]))
return nil;
return [valueForKey objectAtIndexedSubscript: index];
}
return [super valueForKey: string];
}

Create a plist file in iOS error

I want to write the method which should create for writing a plist file. I got the example code in the Web but can not understand what is wrong with it. First of all, when I try to call this method - I get a message in log:
2013-03-28 15:33:47.953 ECom[6680:c07] Property list invalid for format: 100 (property lists cannot contain NULL)
2013-03-28 15:33:47.954 ECom[6680:c07] An error has occures <ECOMDataController: 0x714e0d0>
Than why does this line return (null)?
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
and the last question - how to remove the warning message for the same line?
Incompatible pointer to integer conversion sending 'void *' to parameter of type 'NSPropertyListWriteOptions' (aka 'insigned int')
h-file
#import <Foundation/Foundation.h>
#interface ECOMDataController : NSObject
{
CFStringRef trees[3];
CFArrayRef treeArray;
CFDataRef xmlValues;
BOOL fileStatus;
CFURLRef fileURL;
SInt32 errNbr;
CFPropertyListRef plist;
CFStringRef errStr;
}
#property(nonatomic, retain) NSMutableDictionary * rootElement;
#property(nonatomic, retain) NSMutableDictionary * continentElement;
#property(nonatomic, strong) NSString * name;
#property(nonatomic, strong) NSString * country;
#property(nonatomic, strong) NSArray * elementList;
#property(nonatomic, strong) id plistData;
#property(nonatomic, strong) NSString * plistPath;
#property(nonatomic, strong) NSData * data;
#property(nonatomic, strong) id filePathObj;
-(void)CreateAppPlist;
#end
m-file
#import "ECOMDataController.h"
#implementation ECOMDataController
#synthesize rootElement, continentElement, country, name, elementList, plistData, data, plistPath;
- (void)CreateAppPlist {
// Get path of data.plist file to be created
plistPath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
// Create the data structure
rootElement = [NSMutableDictionary dictionaryWithCapacity:3];
NSError *err;
name = #"North America";
country = #"United States";
continentElement = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:name, country, nil] forKeys:[NSArray arrayWithObjects:#"Name", #"Country", nil]];
[rootElement setObject:continentElement forKey:#""];
//Create plist file and serialize XML
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];
if(data)
{
[data writeToFile:plistPath atomically:YES];
} else {
NSLog(#"An error has occures %#", err);
}
NSLog(#"%# %# %#", plistPath, rootElement, data);
}
#end
It seems that you are serializing the wrong element, replace plistData by rootElement (and nil by 0) in
data = [NSPropertyListSerialization dataWithPropertyList:plistData format:NSPropertyListXMLFormat_v1_0 options:nil error:&err];

Resources