Convert Swift method using extend and append functions to Objective-C - ios

I have this Swift method that I want to convert to Objective-C. The method is using the extend method of Array and as far as I understand it just adds an object to the array while calling itself again.
Swift:
///the method to serialized all the objects
func serializeObject(object: AnyObject,key: String?) -> Array<HTTPPair> {
var collect = Array<HTTPPair>()
if let array = object as? Array<AnyObject> {
for nestedValue : AnyObject in array {
collect.extend(self.serializeObject(nestedValue,key: "\(key!)[]"))
}
} else if let dict = object as? Dictionary<String,AnyObject> {
for (nestedKey, nestedObject: AnyObject) in dict {
var newKey = key != nil ? "\(key!)[\(nestedKey)]" : nestedKey
collect.extend(self.serializeObject(nestedObject,key: newKey))
}
} else {
collect.append(HTTPPair(value: object, key: key))
}
return collect
}
What I've done so far in Objective-C.
- (NSArray*) serializeObject:(id)obj key:(NSString*)key
{
NSMutableArray* collect = [NSMutableArray array];
if ([obj isKindOfClass:[NSArray class]])
{
NSArray* objArray = obj;
if (obj)
{
for (id nestedObj in objArray)
{
[collect addObject:[self serializeObject:nestedObj key:[NSString stringWithFormat:#"%#[]", key]]];
}
}
}
else if ([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary* dict = obj;
if (dict)
{
for (NSString* nestedKey in dict)
{
NSString* newKey = key != nil ? [NSString stringWithFormat:#"%#[%#]", key, nestedKey] : nestedKey;
id nestedObject = [dict objectForKey:newKey];
if (nestedObject)
{
[collect addObject:[self serializeObject:nestedObject key:newKey]];
}
}
}
}
else
{
[collect addObject:[[WEEHTTPPair alloc] initWithValue:obj andKey:key]];
}
return collect;
}
The goal is to get an NSArray of WEEHTTPPair objects for every key/value pair in the dictionary but I lose the meaning of extend and append to apply in my Objective-C code. For me it looks like both are adding the object to the array which is created new anyway but it's more that I lack in knowledge so far.
[EDIT]
The method is used accordingly.
Swift.
///convert the parameter dict to its HTTP string representation
func stringFromParameters(parameters: Dictionary<String,AnyObject>) -> String {
return join("&", map(serializeObject(parameters, key: nil), {(pair) in
return pair.stringValue()
}))
}
I converted to Objective-C borrowed BlockKits map extension.
Objective-C.
- (NSString*) stringFromParameters:(NSDictionary*)parameters
{
WEENSArrayBlocksKit* blockKit = [WEENSArrayBlocksKit new];
NSArray* serializedParams = [self serializeObject:parameters key:nil];
NSArray* arrayParams = [blockKit bk_map:serializedParams withBlock:^id(id obj)
{
// obj is an array without the desired results
WEEHTTPPair* httpPair = obj;
NSString* stringValue = nil;
if (httpPair)
{
stringValue = [httpPair stringValue];
}
return stringValue;
}];
NSString* joinedString = [arrayParams componentsJoinedByString:#"&"];
return joinedString;
}

When you do this:
[collect addObject:[self serializeObject:nestedObject key:newKey]];
You are adding an NSArray to your collect object. Instead, you want to add the objects contained within the response to collect:
[collect addObjectsFromArray:[self serializeObject:nestedObject key:newKey]];

Related

How to get a value from dictionary which is inside an array?

I want to store a message - "hi this is John KL", in some string, how to parse following example.
[
{
"message": "hi this is John KL"
}
]
Swift:
guard let anArray = input as? [[String:String]],
let message = anArray.first["message"] else {
print("unable to fetch data"
}
Objective-C:
- (void) readJSON {
NSError *result;
NSURL *url = [[NSBundle mainBundle] URLForResource: #"sample"
withExtension: #"JSON"];
NSData *data = [NSData dataWithContentsOfURL: url
options: 0
error: &result];
if (result != nil) {
NSLog(#"Error reading file: %#", result);
return;
}
NSArray<NSDictionary<NSString*, NSString*> *> *array = [NSJSONSerialization JSONObjectWithData: data options: 0 error: &result];
if (result != nil) {
NSLog(#"Error converting JSON: %#", result);
return;
}
else {
NSLog(#"\nJSON data = \n%#", array);
if (array.count < 1) {
NSLog(#"Not enough elements in array");
return;
}
NSString *message = array[0][#"message"];
if (message == nil) {
NSLog(#"Unable to fetch message");
} else {
NSLog(#"Message = \"%#\"", message);
}
}
}
The above Objective-C code does not test to make sure the object read from the JSON file is the correct type. It will crash if it is not an array containing a dictionary with a string key and string value. For a production app you'll want to add code to type-check the data.
Swift
Assign your json array to a variable type of [String : String] or [String : Any] dictionary array. [String : Any] is most commonly used dictionary but according to your data it suites with [String : String]
if let array = [
{
“message” : “hi this is John KL”
}
] as [Any]
Now, get your json/dictionary from array using index value and then get string from json using json key.
if let dictionary = array.first as? [String : Any] {
if let stringMessage = dictionary["message"] as? String {
print("stringMessage - \(stringMessage)")
}
}
Objective-C
NSArray * array = [
{
“message” : “hi this is John KL”
}
];
NSDictionary * dictionary = (NSDictionary *)[array objectAtIndex: 0];
NSString * stringMessage = (NSString *)[dictionary valueForKey: "message"];
NSLog(#"stringMessage - %#",stringMessage);
You can try this answer.
NSArray *result = [json objectForKey:#"result"];
for(NSString *currenObject in result){
NSLog(#"%#",currenObject);
NSString *currentValue = [currenObject valueForKey:#"message"];
}
NSLog(#"%#",currentValue);

How can I add search option on UIPickerview in ios?

I am using UIActionsheet uipickerview in my project. Here, I am passing value according to his id.
I mean, in uipickerview list it will show name, and after passing it takes its id.
So, for 5-10 values it is ok, but suppose I have more than 100 values with its id, then it takes to much time to scroll, find and select a value.
So, I want to add search option in it. So after clicking my Text Field that picker or pop up will come. And it shows some list first, as well as search option available there so user can type any one or two words, so according its words data will come according to word (auto search complete).
How can I implement it.
If Anyone wants it in swift...
Create a textField with name txtSearch and then in ViewDidload add below two lines
self.txtSearch.delegate=self
self.txtSearch.addTarget(self, action: #selector(self.textFieldValueChanged(TextField:)), for: UIControlEvents.editingChanged)
func textFieldValueChanged(TextField:UITextField)
{
if((TextField.text?.characters.count)!>0)
{
self.filteredArray = (self.searchInArray(srchArray: self.ArraywithFullData, withKey: "key value", Characters: TextField.text!))!
}
else
{
self.filteredArray = self.ArraywithFullData.mutableCopy() as! NSMutableArray
}
self.pickerView.reloadData()
}
func searchInArray(srchArray:NSMutableArray, withKey:String,Characters:String)->NSMutableArray
{
let resultArray=NSMutableArray()
for index in 0..<srchArray.count
{
let Dict = srchArray[index] as! [String:Any]
if let stringmatcher = Dict[withKey] as? String
{
if(stringmatcher.contains(find: Characters))
{
resultArray.add(Dict)
}
else
{
}
}
}
return resultArray
}
extension String {
func contains(find: String) -> Bool{
return self.range(of: find) != nil
}
And For Objective-C You have to Do something Like below. But don't forget to call textField Delegates.
NSMutableArray *filteredArray;
NSMutableArray *ArraywithFullData;
self.txtSearch.delegate=self
[self.txtSearch addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
//
-(void)textFieldValueChanged:(UITextField *)TextField
{
if((TextField.text.length)>0)
{
filteredArray = [self searchInArray:ArraywithFullData withKey:#"key value" andCharacters:TextField.text];
}
else
{
filteredArray = [ArraywithFullData mutableCopy];
}
[self.pickerView reloadData];
}
-(NSMutableArray *)searchInArray:(NSMutableArray*)srchArray withKey:(NSString *)key andCharacters:(NSString *)charecters
{
NSMutableArray *resultArray= [[NSMutableArray alloc]init];
for (int index=0 ; index<srchArray.count; index++)
{
NSMutableDictionary *Dict = srchArray[index];
if ([Dict valueForKey:key] != nil)
{
NSString * stringmatcher = [Dict valueForKey:key];
if ([stringmatcher containsString:charecters])
{
[resultArray addObject:Dict];
}
else
{
}
}
}
return resultArray;
}
Note if data is not in form of array of Dictionaries you can remove code work that is about dictionaries

Check if string is already exist and add a number as suffix to the string in the array?

I have a name as "Ryan" and next time entering "Ryan" it should check it has the value,So it should make it "Ryan_1". The same way it should check if anytime someone added again "Ryan" it should change it to "Ryan_2".
Example:
nameArray = ["Ryan","John","Ryan_2","Rhonda","Ryan_3","Kylie","Ryan_4","John_2"];
I am using below code which is working fine while adding the name first time.
But when I am coming back to the section and editing value Ex: I changed Ryan_2 to "xyz" and again thought of keeping "Ryan" the saved value is becoming "Ryan_4" where as it supposed to be "Ryan_2" in the array. And let say if I am changing "Ryan_4" to "somenewName" now the numbering of other duplicate names also get rearranged.
NSMutableArray *array = [[NSMutableArray alloc]init];
int occurrences = 0;
for(NSString *string in nameArray) {
if ([value isKindOfClass:[NSString class]]) {
if ([string containsString:value]) {
[array addObject:string];
}
occurrences+= ([string containsString:value] ? 1 : 0);
}
}
if ([value isKindOfClass:[NSString class]]) {
if (![value isEqualToString:#""]) {
if (occurrences > 1) {
value = [value stringByAppendingFormat:#"_%d", occurrences];
}
}
}
I would recommend an extension like this:
extension NSMutableArray {
public func appendWithSuffix(strNewEntry:String) {
var n = 1
var new = strNewEntry
if self.containsObject(strNewEntry) {
new = strNewEntry + "_\(n)"
while self.containsObject( new ){
n += 1
new = strNewEntry + "_\(n)"
}
}
self.addObject( new )
}}
It will look for an exact match and insert if none is found, i.e it will add "Ryan_5" instead of "Ryan", "John_3" instead of "John" and so on.
Another approach may be to iterate thru all entries for comparison or filter the array with "namexy".hasPrefix("Ryan") to get max index of name to be inserted.
- (IBAction)SaveText:(id)sender
{
if (array.count==0)
{
[array addObject:_txtAddText.text];
}else
{
for(NSString *string in array)
{
if ([string containsString:_txtAddText.text] )
{
count = count+1;
NSString *Localstring =[NSString stringWithFormat:#"%#%d",[string stringByAppendingString:#"_"],count];
NSLog(#"%#",Localstring);
[array addObject:Localstring];
break;
}else
{
[array addObject:_txtAddText.text];
}
}
}
NSLog(#"%#",array);
}

Crashing app due to null string added to dictionary

func addSongToQueue(){
let post = [
"data": [
"QueueId": MyViewState.selectedQueId,
"track":[
[
"title":selectedTrack.title,
"stream_url": selectedTrack.stream_url,
"userName" : selectedTrack.userName,
"artWorkURL": selectedTrack.artWorkURL,
"userAvatar": selectedTrack.userAvatar,
"trackID" : selectedTrack.trackID,
"duration" : selectedTrack.duration
]]]
]
Tracks Getting Code :
-(instancetype) initWithDictionary: (NSDictionary*) SCTrackDict {
self = [self init];
if (self) {
self.title = SCTrackDict[#"title"];
self.stream_url = SCTrackDict[#"stream_url"];
self.userDict = SCTrackDict[#"user"];
self.userName = self.userDict[#"username"];
self.artWorkURL = SCTrackDict[#"artwork_url"];
self.trackID = SCTrackDict[#"id"];
self.userAvatar = self.userDict[#"avatar_url"];
self.duration = SCTrackDict[#"duration"];
}
return self;
}
Parsing Data into Tracks :
+(NSMutableArray *) parseJSONData: (NSData *) JSONData {
NSError* error;
NSMutableArray* SCTrackArray = [NSMutableArray new];
NSArray *JSONArray= [NSJSONSerialization JSONObjectWithData:JSONData options:0 error: &error];
if ([JSONArray isKindOfClass:[NSArray class]]) {
for (NSDictionary* trackDict in JSONArray) {
SCTrack* trackObject = [[SCTrack alloc]initWithDictionary:trackDict];
//Need to print out trackDict to see JSON dictionary
[SCTrackArray addObject:trackObject];
}
}
return SCTrackArray;
}
How can I add "" (empty string) if NSNull is there and also crashing on trackid as it is long value.
Is it possible to modify this code without checking individual key before adding in dict?
Clarify to yourself: What action do you want to take if a key that you expect is not present, or is [NSNull null], or is an empty string, or is a dictionary when you expected a string and so on and so on.
Once you've explained that to yourself, you write the code accordingly. It seems at the moment your code follows the rule "If it's not there, I'll crash".
before adding an object just check like this that is this null or not
if(yourObject isEqual:(id)[NSNUll null])
{
// Then do nothing
}
You can use something like this:
"stream_url": selectedTrack.stream_url != nul ? selectedTrack.stream_url : #"";
Hope it help.
You can read about Ternary operation

Check key exists in NSDictionary

how can I check if this exists?:
[[dataArray objectAtIndex:indexPathSet.row] valueForKey:#"SetEntries"]
I want to know whether this key exists or not. How can I do that?
Thank you very much :)
EDIT:
dataArray has Objects in it. And these objects are NSDictionaries.
I presume that [dataArray objectAtIndex:indexPathSet.row] is returning an NSDictionary, in which case you can simply check the result of valueForKey against nil.
For example:
if ([[dataArray objectAtIndex:indexPathSet.row] valueForKey:#"SetEntries"] != nil) {
// The key existed...
}
else {
// No joy...
}
So I know you already selected an answer, but I found this to be rather useful as a category on NSDictionary. You start getting into efficiency at this point with all these different answers. Meh...6 of 1...
- (BOOL)containsKey: (NSString *)key {
BOOL retVal = 0;
NSArray *allKeys = [self allKeys];
retVal = [allKeys containsObject:key];
return retVal;
}
Check if it's nil:
if ([[dataArray objectAtIndex:indexPathSet.row] valueForKey:#"SetEntries"] != nil) {
// SetEntries exists in this dict
} else {
// No SetEntries in this dict
}
this also works using Objective-C literals using the following syntax:
NSDictionary *dict = #{ #"key1" : #"value1", #"key2" : #"value2" };
if (dict[#"key2"])
NSLog(#"Exists");
else
NSLog(#"Does not exist");
Try this:
if ([dict objectForKey:#"bla"]) {
// use obj
} else {
// Do something else like create the object
}
This one does the same but with less code:
if (dataArray[indexPathSet.row][#"SetEntries"] != nil) { /* the key exists */ }
else { /* the key doesn't exist */ }
if ((NSNull *)[[dataArray objectAtIndex:indexPathSet.row] valueForKey:#"SetEntries"] != nil) {
// SetEntries exists in this dict
} else {
// No SetEntries in this dict
}
That's the right answer.
Check dictionary contains any value. I prefer [dic allKeys].count > 0 to check.
Use the (unsigned long) option:
if ( (unsigned long)[[dataArray objectAtIndex:indexPathSet.row] valueForKey:#"SetEntries"] ) {
// Key exist;
}else{
// Key not exist;
};

Resources