I have a .plist
file like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>key1</key>
<string>variable1</string>
<key>key2</key>
<array>
<string>foobar</string>
<string>variable2</string>
</array>
</dict>
</plist>
My code looks like this:
NSString *variable1 = @"Hello";
NSString *variable2 = @"World!";
NSMutableDictionary *myDict = [NSMutableDictionary dictionaryWithContentsOfFile:@"/path/to/file.plist"];
NSLog(@"key1: %@", [myDict valueForKey:@"key1"]);
NSLog(@"key2: %@", [[myDict valueForKey:@"key2"] objectAtIndex:1]);
// outputs
// key1: variable1
// key2: variable2
What I'm hoping to do is have the variable1
and variable2
in the .plist
file use the variable1
and variable2
from the code (without specifically doing it in code), so the values from the .plist
end up being Hello
for variable1
and World!
for variable2
.
Obviously assigning them like this is possible:
[myDict setObject:variable1 forKey:@"key1"];
That's exactly what I'm trying to avoid. Clearly the "variables" are merely strings in the .plist
and obj-c doesn't think to assign them the values of the variables from the code. So the question is can I assign them the values from the code (like <variable>variable1</variable>
), or somehow not make them strings? Or maybe this just isn't possible.
Note: I'm not a programmer, so please keep this in mind, thank you.
So the question is can I assign them the values from the code (like variable1), or somehow not make them strings?
Many ways, but...
Note: I'm not a programmer, so please keep this in mind, thank you.
which presents obvious challenges! We've no idea how much you actually know, you've presented no attempt to do the actual substitution, etc. However you do make a good observation:
Clearly the "variables" are merely strings in the .plist and obj-c doesn't think to assign them the values of the variables from the code.
Many beginners, and a few who aren't, confuse the difference between the character sequence variable1
used as the name for a variable and the same sequence used as the value for a string – you correctly identify these are different things.
Let's see if we can help you along without a long lesson on program design. First we'll stick with your use of a plist for your dictionary (later given your suggestion of <variable>variable1</variable>
you may wish to consider using your own XML).
Second we'll make the assumption that when a "variable" is used as a value in your dictionary it will always be the whole value and not part of it, e.g. you'll never have something like <key>key1</key> <string>The value of the variable is: variable1</string>
and expect variable1
to be replaced.
We start by modifying your code to read the plist from the app bundle and using modern Objective-C syntax to look up dictionary and array values:
NSURL *sampleURL = [NSBundle.mainBundle URLForResource:@"sampleDictionary" withExtension:@"plist"];
NSMutableDictionary *myDict = [NSMutableDictionary dictionaryWithContentsOfURL:sampleURL];
NSLog(@"key1: %@", myDict[@"key1"]); // dictionary lookup
NSLog(@"key2: %@", myDict[@"key2"][1]); // dictionary + array lookup
The file sampleDicitonary.plist
has been added to the add and contains your plist. This outputs:
key1: variable1
key2: variable2
just as your code does.
Now instead of declaring your two variables, variable1
and variable2
, you can use a dictionary where the key is the variable name:
NSDictionary *variables = @{ @"variable1": @"Hello",
@"variable2": @"World!"
};
This represents the same information as your two distinct variables but crucially the variable names are just strings, and as you've already observed your plist contains strings – so now we have a problem of string substitution rather than variable substitution. To do the string substitution we utilise:
myDict[@"key1"]
above, returns nil
if the key does not exist in the dictionary;<expr1> ?: <expr2>
evaluates to the value of a <expr1>
if the value is not nil
, others it evaluates to <expr2>
(This is a contraction of the conditional expression – which you can look up.)So if we assign the result of indexing into your plist to variable:
NSString *rawKeyValue = myDict[@"key1"];
and then look that value up in our variables
dictionary:
NSString *substitutedValue = variables[rawKeyValue];
then if rawKeyValue
has a value of one of the names your "variables" (variable1
, variable2
) then substitutedValue
will be the corresponding value (Hello
, World!
). If rawKeyValue
is not a variable name then substitutedValue
will be nil
...
But you don't want nil
in the second case, you want the value of rawKeyValue
and you can get that by utilising the ?:
operator which gives a replace of the first NSLog()
of:
NSString *rawKeyValue = myDict[@"key1"];
NSString *substitutedValue = variables[rawKeyValue] ?? rawKeyValue;
NSLog(@"key1: %@", substitutedValue);
which will output:
key1: Hello
Doing these extra steps for every lookup is repetitive and tedious, and programming has a solution to that: define a method to encapsulate it and avoid using the intermediate variables. Here is a possible method:
- (NSString *)substitute:(NSDictionary *)variables in:(NSString *)value
{
return variables[value] ?: value;
}
and with that defined the first NSLog
becomes:
NSLog(@"key1: %@", [self substitute:variables in:myDict[@"key1"]]);
Hope that helps and gets you started. But do spend time learning programming properly if you're going to write programs!