Reading strings from a text file in Objective-C - ios

The game is similar to the quiz game. Questions are pictures and answers are strings.
Just wondering what would be the best way to read strings(answer) from the text file randomly, in order to use the string selected to pull up pictures(questions) from a set of pictures. Pictures will have the same names as all the name strings in the text file, however I can't have them repeat.
As of now I have switch statement that has multiple cases that select the picture(question) and strings(answers). Basically I don't want to keep all the strings in code in a .m file.
The question will be in a form of a picture and a text file will hold answers.
answers.txt
gta
fifa
minecraft
Questions:
gta.jpg
fifa.jpg
minecraft.jpg
so the randomizer will for example pick
answer gta
and when it does so, it should select the right pic(gta.jpg)
so at the end it will look like this:
gta.jpg
and four answers choices including the gta and the player will pick the right answer
is this clear?

Use a property list. Store the list of questions as an array of dictionaries, where each dictionary has entries for the question file name and the answer, like this:
[
{
"question" : "gta.jpg",
"answer" : "gta"
},
{
"question" : "fifa.jpg"
"answer" : "fifa"
},
//...
]
Then you can read the dictionary into memory using a convenience method:
NSArray *questions = [NSArray arrayWithContentOfFile:pathToQuestionsPList];

I think Caleb's suggestion is very good. By having an array of dictionaries, string and it's image are always kept together.
You could write a one-time parser method that would take a text file as input and generate your output plist. I'm thinking your file would be multiple lines of imageNameanswer. You'd then read the text file, use the NSString method componentsSeparatedByString to break it up into lines by line break, and then loop through the lines, again using componentsSeparatedByString: #"\i" (the tab character), this time to break it into a filename and an answer string. You turn the results into an array of dictionaries and write it out to your app's documents folder. Then just drag the result into your project.
If you wanted to get really fancy you could turn your text file parser into a command-line tool, and make it part of the build process so that when you update your text file of image names and answers, the build process automatically runs the parser on it and copies the output into the application bundle. Methinks that's a little beyond your current abilities however.

If you have a lot of questions you probably want to learn to use core data and a database. If you only have a few, then the plist or dictionary method will work.
One of my apps has a bunch of stories in a database and at the last minute, we decided to add images. Rather than messing with the database, I wrote a quick class that uses a dictionary to pair the story with the an image name.
The view controller queries the class to get the name of the image.
NSString *imageName = [EarlyReadingImageNames findStoryImage:title];
This is the full class.
//
// EarlyReadingImageNames.m
// Words
//
// Created by John Scarry on 5/20/14.
//
#import "EarlyReadingImageNames.h"
#implementation EarlyReadingImageNames
+(NSString *) findStoryImage:(NSString *)story {
NSDictionary* imageDictionary= #{ #"Alice the Worker Bee" : #"Alice",
#"Alice Learns to Fly" : #"Alice",
#"Alice Loves Her New Job" : #"Alice",
#"George Likes to Sing" : #"George",
#"George Likes to Dance" : #"George",
#"George Saves the Day" : #"George",
#"Jensen Meets Bob the Buffalo" : #"JensenBob",
#"Jensen and Bob Play in the Pond" : #"JensenBob",
#"Jensen and Bob Make a Pair of Boots" : #"JensenBob",
#"Rita Finds a New Home" : #"Rita",
#"Rita Makes a Boat" : #"Rita",
#"Rita Loves Words" : #"Rita",
#"The Rock That Looked Like a Frog" : #"Sandy",
#"The Rock and the Rainbow" : #"Sandy",
#"Sandy Makes New Friends" : #"Sandy",
#"James and the Bowl of Baseballs" : #"James",
#"James and the Garden" : #"James",
#"James Builds a Bird House" : #"James",
#"Lily Finds Eggs" : #"Lily",
#"Lily and Bessie the Cow" : #"Lily",
#"Lily Feeds the Lambs" : #"Lily",
#"Hector and Bo" : #"Hector",
#"Hector Loves Fish Socks" : #"Hector",
#"Hector Makes a Kite" : #"Hector",
#"Yoshi and Toshiko Get a New Home" : #"ToshikoYoshi",
#"Yoshi and Toshiko Go to the Library" : #"ToshikoYoshi",
#"Yoshi and Toshiko Go to the Park" : #"ToshikoYoshi",
#"Pete Loves Birds" : #"Pete",
#"Pete Meets Max" : #"Pete",
#"Pete and Max Are Best Friends" : #"Pete"
};
return [imageDictionary valueForKey:story];
}
#end

Related

How to make firebase list like this on swift

This is what I want it to be
first I did it like this:
self.ref.child("User/CareGiver/\(CaregiverUID)/Followed").setValue([value2])
the result looks like in the picture. but when I add second data. It replaces the old one.
so I change to this
self.ref.child("User/CareGiver/\(CaregiverUID)").updateChildValues([
"Followed" : ["\(value2)"]])
it still replaces data at position[0] and never make it to position 1
how can I do
array [UID1,UID2,UID3] to firebase (not add array data at the same time so it would be like this)
-[0] UID1
-[1] UID2
-[2] UID3
without replacing another one?
ps.sorry for broken English
JSON should look likes this
{
"Name_Care" : "asdsท",
"Tel_Care" : "kknlk",
"Role" : "Care Giver",
"Followed" : [
"UID_A",
"UID_B",
"UID_C",
"UID_E"
]
}
in firebase would be like in picture
So if you want to add new values to your "followed" dictionary, you should use "setValue" function with uniq id for your new user.
Example:
self.ref.child("User/CareGiver/\(CaregiverUID)/Followed").setValue("3":"user_name")
In this case you add record user_name for key 3
I think this way can help you.

Objective-C iOS parse string according to the definition

I want create an iOS app for my school.
This App will show Week schledule and when I tap on Cell with Subject, it will show me detail info about subject...
My problem:
Our teachers use shotcuts for their names, but I want show their full name... I created the file "ucitele.h" with definitions of their names, but I don't know, how to use it 😕.
This is how that file looks:
//
// ucitele.h
//
#define Li #"RNDr. Dan---vá"
#define He #"Mgr. Ja---hl"
#define Sm #"Ing. Mich---rek"
#define Ks #"Mgr. Svat---á"
I get the shortcut of Teacher from previous view from "self.ucitel" and I maybe want compare the contents of the "self.ucitel" with definitions and set the "ucitelFull" string from the definitions? I don't know how to say it 😕.
when the content of the self.ucitel will be #"Sm", than I want parse "ucitelFull" as #"Ing. Mich---rek"
Answers in Objective-C only please
Okay, sounds like your trying to map a short identifier to a full name:
-(NSString*)fullNameFromShortName:(NSString*)short {
NSDictionary * names = #{#"Li" : #"RNDr. Dan---vá",
#"He" : #"Mgr. Ja---hl", ... };
return [names objectForKey:short];
}
Use like:
self.ucitelFull = [self fullNameFromShortName:self.ucitel];
This is a dictionary that has the short name as a key and the full name as the value.
Some further suggestions:
try using lowercase keys and comparing lowercaseString's, incase the user doesn't enter the value with the correct case.
You can move the dictionary definition into a json file and read it from your bundle, to eliminate the hardcoding

Swift App Takes ~6 Minutes To Build

I have a Swift app with an array of about ~100k strings. The array looks something like this:
let strings: [String] = [
"a",
"as",
// 99,998 elements later...
"zebra"
]
It takes nearly 6 minutes to build and run the app in the iOS Simulator. I've isolated the slow build time to the inclusion of this array in the project. Once built, subsequent launches are very fast (until I have to build again). What can I do to speed up the build process?
Per the comments above, the best solution for me was to use a text file. A database would have worked too, though it would've added unnecessarily complexity in this case. The text file looks something like this:
a
as
...
zebra
The file is read using the StreamReader gist from this SO post. The code for doing so looks like this:
if let aStreamReader = StreamReader(path: "/path/to/file") {
for word in aStreamReader {
// This is where I'm testing the following conditions:
// 1) Does the word have a specific number of characters (e.g. 4 or 7)?
// 2) Do all the characters in the word exist in a stored string?
// e.g "lifeline", where "file" would match, but "lifelines" wouldn't.
// This code is only here for context.
if contains(characterCountsToMatch, countElements(word)) {
if stringToMatch.containsCharsInString(word) {
matchingWords.append(word)
}
}
}
}
The resulting matchingWords array contains only the necessary elements–about 600 in my case (not ~100k elements!). The application now compiles with no delay. Reading from the file and appending matches to the matchingWords array takes about 5 seconds, which is acceptable for my needs, but could be further optimized if needed.

Phonegap/Sencha Language Localization

The Problem:
I'm about to implement language localization to an already very large ipad application that was built using sencha touch wrapped in phonegap. I have the english and spanish translations in json files.
What I'm Planning on Doing:
I plan to load the json files into a sencha touch store, creating a global object. Then in each place where I call text that is displayed, i will replace the text with a call to the global object.
My question(s):
Is there an easier way to implement language localization with my
setup?
Will I run into issues with native sencha stuff (like datepickers)?
When loading/reloading language json files, will I have performance
issues (require a webview reload?, sencha object resizing issues,
etc)
edit 1 : Useful Related Info:
For those going down this road, it quickly becomes useful to write a simple phonegap plugin to get the ipad/iphone device's language setting into your javascript. This requires a plugin, which will look something like this:
Javascript :
part 1:
PhoneGap.exec("PixFileDownload.getSystemLanguage");
part 2(callback Function):
setLanguage(returnedLanguage)
{
GlobalVar.CurrentLanguage = returnedLanguage; //GloablVar.CurrentLanguage already defined
}
Objective C:
-(void)getSystemLanguage:(NSMutableArray*)paramArray withDict:(NSMutableDictionary*)options
{
/*Plugin Details
PhoneGap.exec("PixFileDownload.getSystemLanguage");
Returns Language Code
*/
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString *language = [languages objectAtIndex:0];
NSLog(#"####### This is the language code%#",language);
NSString *jsCallBack;
jsCallBack = [NSString stringWithFormat:#"setLanguage('%#');",language];
[self.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
}
edit 2: character encoding
When adding additional language characters to a sencha project (or any webview phonegap project), ensure that you have the proper encoding specified in the index file. This is the tag i needed.
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
I've finished this language localization plugin. It's not amazing, but it worked better than I originally speculated. Here are the short answers to each of the questions.
1- Is there an easier way to implement language localization with my
setup?
Not that i'm aware of. The comment by Stuart provided this link Sencha-touch localization. Use a store or a global JSON object? which had some good information on one way that you can use class overrides. I didn't like this approach, because it spread my language translations into different classes. But certainly, if you are doing something simple, or you want something that could be more powerful, perhaps you should investigate that.
2- Will I run into issues with native sencha stuff (like datepickers)?
I ended up specifically leaving "datepickers" in english for now. But everything else was really relatively easy to customize. Almost every graphical UI element can have it's text altered.
3- When loading/reloading language json files, will I have performance
issues (require a webview reload?, sencha object resizing issues,
etc).
The method that i employed (see below) worked exceptionally well in regards to performance. The one issue that you have is right when you switch the languages, you need that specific page to reload. Sencha handled resizing without any flaws, except where I had been foolish and statically set sizes.
Some of what I did was described in edits to the question. Here is a detailed overview of my solution. (warning, it's not the most elegant solution.)
Instead of using a pure JSON file, I ended up just using a javascript function. This isn't the greatest solution because it requires some minimal maintenance, but JSON parsing with phonegap/sencha isn't the best. (I get JSON files from translater's, and quickly paste into the javascript file. Takes around 2 minutes, see further explanation below).
Language.js
function setLanguage(language)
{
if(language == "en")
{
//console.log("inside if Language == en");
GlobalLanguage.CurrentLanguage = language;
GlobalLanguage.ID = {"glossary": [
{
//CONVERTED JSON
about : 'About',
checking_for_updates : 'Checking for updates...(This may take a few minutes.)'
//Any additional translations
}
]};
}
if (language == "es"){
//console.log("inside language == es");
GlobalLanguage.CurrentLanguage = language;
GlobalLanguage.ID = {"glossary": [
{
//CONVERTED JSON
about : 'Acerca de ',
checking_for_updates : 'Verificando actualizaciones... (Capas que demore algunos minutos).'
//Any additional translations
}]};
}
if (language == "pt"){
//console.log("inside language == pt");
GlobalLanguage.CurrentLanguage = language;
GlobalLanguage.ID = {"glossary": [
{
//CONVERTED JSON
about : 'Sobre',
checking_for_updates : 'Verificando se há atualizações... (pode demorar alguns minutos.)'
//Any additional translations
}]};
}
}
As you can see, this file allows for 3 languages (Portugese, English, and Spanish). After setting the language, you could access each localized string anywhere in your object. For example, if you need to access the word "about" simply use:
GlobalLanguage.ID.glossary[0]["about"]
This will access the GlobalLanguage object which will have whatever language loaded into the properties. So throughout your project, you could have these calls. However, I'd recommend taking it one step further
function langSay(languageIdentifier){
// console.log("inside langSay");
if(!GlobalLanguage.ID.glossary[0][languageIdentifier]){
return "[! LANGUAGE EXCEPTION !]";
}
else{
return GlobalLanguage.ID.glossary[0][languageIdentifier];
}
}
This protects you from having language exceptions and having your program crash without knowing where (you may have hundreds or thousands of properties being set in that language.js file). So now simply :
langSay("about")
One additional note about formatting from JSON. The format you want your language files in is:
languageIdentifier : 'Translation',
languageIdentifier : 'Translation',
languageIdentifier : 'Translation'
I used Excel for the formatting. Also the languageIdentifiers are unique identifiers without spaces. I recommend just using Excel to format first 3 to 4 words word1_word2_word3_word4 of the english translation.
word1_word2_word3 : 'word1 word2 word3'
Hope this helps you out! I'd be happy to respond to any questions

Best way of storing an "array of records" at design-time

I have a set of data that I need to store at design-time to construct the contents of a group of components at run-time.
Something like this:
type
TVulnerabilityData = record
Vulnerability: TVulnerability;
Name: string;
Description: string;
ErrorMessage: string;
end;
What's the best way of storing this data at design-time for later retrieval at run-time? I'll have about 20 records for which I know all the contents of each "record" but I'm stuck on what's the best way of storing the data.
The only semi-elegant idea I've come up with is "construct" each record on the unit's initialization like this:
var
VulnerabilityData: array[Low(TVulnerability)..High(TVulnerability)] of TVulnerabilityData;
....
initialization
VulnerabilityData[0].Vulnerability := vVulnerability1;
VulnerabilityData[0].Name := 'Name of Vulnerability1';
VulnerabilityData[0].Description := 'Description of Vulnerability1';
VulnerabilityData[0].ErrorMessage := 'Error Message of Vulnerability1';
VulnerabilityData[1]......
.....
VulnerabilityData[20]......
Is there a better and/or more elegant solution than this?
Thanks for reading and for any insights you might provide.
You can also declare your array as consts and initialize it...
const
VulnerabilityData: array[Low(TVulnerability)..High(TVulnerability)] of TVulnerabilityData =
(
(Vulnerability : vVulnerability1; Name : Name1; Description : Description1; ErrorMessage : ErrorMessage1),
(Vulnerability : vVulnerability2; Name : Name2; Description : Description2; ErrorMessage : ErrorMessage2),
[...]
(Vulnerability : vVulnerabilityX; Name : NameX; Description : DescriptionX; ErrorMessage : ErrorMessageX)
)
);
I don't have an IDE on this computer to double check the syntax... might be a comma or two missing. But this is how you should do it I think.
not an answer but may be a clue: design-time controls can have images and other binary data associated with it, why not write your data to a resource file and read from there? iterating of course, to make it simpler, extensible and more elegant
The typical way would be a file, either properties style (a=b\n on each line) cdf, xml, yaml (preferred if you have a parser for it) or a database.
If you must specify it in code as in your example, you should start by putting it in something you can parse into a simple format then iterate over it. For instance, in Java I'd instantiate an array:
String[] vals=new String[]{
"Name of Vulnerability1", "Description of Vulnerability1", "Error Message of Vulnerability1",
"Name of Vulnerability2", ...
}
This puts all your data into one place and the loop that reads it can easily be changed to read it from a file.
I use this pattern all the time to create menus and for other string-intensive initialization.
Don't forget that you can throw some logic in there too! For instance, with menus I will sometimes create them using data like this:
"^File", "Open", "Close", "^Edit", "Copy", "Paste"
As I'm reading this in I scan for the ^ which tells the code to make this entry a top level item. I also use "+Item" to create a sub-group and "-Item" to go back up to the previous group.
Since you are completely specifying the format you can add power later. For instance, if you coded menus using the above system, you might decide at first that you could use the first letter of each item as an accelerator key. Later you find out that File/Close conflicts with another "C" item, you can just change the protocol to allow "Close*e" to specify that E should be the accelerator. You could even include ctrl-x with a different character. (If you do shorthand data entry tricks like this, document it with comments!)
Don't be afraid to write little tools like this, in the long run they will help you immensely, and I can turn out a parser like this and copy/paste the values into my code faster than you can mold a text file to fit your example.

Resources