Discover the environment and relative path of the running application - objective-c

While playing with RubyCocoa, I keep progressing with my idea for my application. Because my application will be going to use configuration files, I would like to know how I discover the relative path to store these inside my application structure (or if a better idea emerges, please elaborate also the "why").
Also good for me to know is to discover environment variables, such as operating system version, the amount of memory that is available and such. Hyperlinks would be awesome too.
Please notice I use RubyCocoa and thank you for your feedback, comments and answers!

To access inside the application bundle, you use NSBundle. See NSBundle reference. In Obj-C, you use +[NSBundle mainBundle] to get the main bundle of the app. Then you use -[NSBundle pathForResource:ofType:] to get the file. I don't know RubyCocoa syntax, but I assume you know how to translate to it :)
If by the configuration file you mean a user-configurable things, remember you can't write inside the app bundle at runtime. Instead one uses NSUserDefaults. See User Defaults Guide.

Here's some Cocoa code I use to write all the environment variables to the console. Again, I don't use RubyCocoa, so you'll have to translate:
NSProcessInfo *myProcessInfo = [NSProcessInfo processInfo];
NSDictionary *env = [myProcessInfo environment];
for (id key in env)
{
NSLog (#"key: %#, value: %#", key, [env objectForKey: key]);
}

Related

Playgrounds for Objective-C

Is it at all possible to have Xcode create a .playground file for Objective-C instead of Swift? Are there any available Xcode plugins that allow that?
You can quickly test code snippets using a test case in a new project. Just create a new project and go to the Navigator in the left pane and hit the Test Navigator button. Then follow this guide
The setup code will look a little different than a swift playground, but it still allows you to prototype and play around.
There is a very good library developed by Krzysztof Zabłocki in Github entitled KZPlayground that support both code in Playgrounds for Objective-C and Swift and a lot of cool features.
I hope this can help you.
If the only purpose is to test out Objective-C snippets, i would really recommend you an OS X command line Tool project.
There are enough moving parts in a playground, and all of those would have to be reimplemented for Objective-C. Reliable playgrounds also depend on definite initialization which Objective-C does not have.
For instance consider:
var d: NSData // this is not initialized, so I can't use it
vs.
NSData *d; // this is also not initialized, but now I can use it
If I am the person storing the description of your NSData for the sidebar, now I know that I am not supposed to do
describe(d)
in the Swift case, but for the Objective-C case, I don't have equal knowledge and I run the risk of saying
[d description]; // even though d is a random pointer now.. oops, I just crashed!
In short, I don't think any such thing exists, and making one work would also involve some trickery

Detect language in use by iOS application

How can I detect the current application language ? I am not talking about NSLocale user preferences.
In my app there are currently two supported languages. A default 'en' and a specific 'it'.
I just wanted to know which one is actually in use. If it is relevant, as a further explanation, I am providing content trough a web service only for the two supported languages. When invoking the service I need to pass a language parameter. Currently I am doing this, it works, but I totally dislike it:
NSString *preferredLanguage = [[NSLocale preferredLanguages] objectAtIndex:0];
if (![preferredLanguage isEqualToString:#"it"] && ![preferredLanguage isEqualToString:#"en"]) {
return #"en";
}
return preferredLanguage;
I have looked trough the NSLocale class and UIApplication, but didn't manage to find anything useful.
I also notice that NSBundle has some localization methods, but they all seems not specific about which one is in use.
[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0] would be better, because it will always return a localization that your app supports. There is no need for an additional check afterwards.
In fact, your code has a bug. If the first language in the user's preferences is not one that your app supports, iOS will continue down the list until it finds one that your app does support. So if the user's preferences were "fr", "it", "en", ..., iOS would load the it versions of your resources. However, your code would fall back to en.
(This is perhaps more important on OS X, where it's easy for the user to change the language ordering. On iOS it's apparently possible to do that, but it's not as obvious how it works.)
I usually reserve a special key in Localizable.strings, such as "HTTPAcceptLanguage", which I set to "en", "fr", etc. Now telling your server the language displayed by the application is as simple as NSLocalizedString(#"HTTPAcceptLanguage", nil).

Objective-C, how to obfuscate or encrypt NSString?

I have an NSString (which is a path to a file) in my code that I would like to somehow obfuscate or encrypt,
but still be able to call up the file path easily when needed.
I searched for an answer to this, but everything I've seen either deals specifically with iOS or seems overly complicated.
I would simply like to use it with something such as this:
- (void)method {
NSString *obfuscate = #"/path/to/something/secret"; // encrypt or obfuscate
[self manageFiles:obfuscate]
- (void)manageFiles(NSString *)obfuscate {
NSFileManager *files = [[NSFileManager alloc] init];
if ([files fileExistsAtPath:obfuscate])
... .
— any help is appreciated, thank you.
(This is an old question, but I'm replying anyway)
There's no such way to in Obj-C. Obj-C is dynamic enough that any of these methods can be trapped and intercepted. Do not ship anything in a application that absolutely needs to be secret. If your application is run on a jailbroken phone, or if it is made available on piracy sites, than it has already been exposed and it's memory contents dumped. All these above methods copy the decoded data to main memory where it is exposed.
See:
https://www.youtube.com/watch?v=Ii-02vhsdVk
None of these methods above is actually secure. Again, do not embed these sorts of things in your applications with an assurance they are actually secure.
What I have done in the past to obfuscate a string was something to this extent:
-(NSString*)myString {
NSString *string = nil;
string = [#"ozzzzzzzzzzzzhazzzzzzzizzzzzz" stringByReplacingOccurrencesOfString:#"z" withString:#""];
return string;
}
What it would do is remove all the occurences of the letter z, leaving you with ohai as a string. Not sure if this will suffice for your case, but it has worked for me.
Hope this helps!

How to find file types in objective c

I have done a lot of research on this and haven't found anything useful. I want am making a simple program in objective c for personal use that opens files and gives information about them. The only problem i have encountered is that i cannot find the file of a file i am opening. Without simply looking at the extension, is there a way to find the full file format of a file in objective c?
If possible, i would also like to be able to save that file in a different format. Information on this subject is also important for this application. Help will be greatly appreciated.
Mac OS X has type information attached to each file which specifies what the type of the file is supposed to be. This information is given by the application which last saved the file, so it is not necessarily correct. Also, new versions of OS X ignore this information and go entirely off of the file extension. However, the information is still stored and can be retrieved using NSFileManager's attributesOfItemAtPath:error: method.
As mentioned by quixoto above, OS X now maps extensions to UTIs. The UTI of a file can be retrieved using NSWorkspace, which can also tell you what the UTI means. This code will get the localized description of the file at /full/path/to/file:
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSString *description = [ws localizedDescriptionForType:[ws typeOfFile:#"/full/path/to/file" error:nil]];
The typeOfFile:error: method requires an absolute path.
Consider: what do you mean by "full file format"? What kinds of files and level of detail do you care about?
You can't really get "format" information from the file system like you want. Believe it or not, the file system has no idea what format files are in.
File systems traditionally store files as a stream of bytes with a name, a size, and some other attributes ("hidden", permissions, etc). That's all that it's responsible for, and it's the application's problem to read those bytes and interpret them to mean something useful.
The extension is the traditional hint to an application about what a file contains, but as you might guess, it's certainly not a verified guarantee.
Modern Mac OS X has Quick Look, which uses a system-wide framework that adds some smarts on top of this, with mappings between extensions and UTIs, which are a richer notion of file type. Docs are here. I don't think there's a way to query this system for the mappings between extensions and UTIs, but I'm not sure.
Loading a file of one format and saving in another is 100% dependent on the file types you're talking about, and you're going to have ask very specific questions about specific formats if you really care to accomplish this. (And that topic extends well beyond a couple built-in method calls in Cocoa.)
This information is now (since OS X 10.5) handled through UTIs.
You can get it like this:
NSURL *desktopURL = [[[NSFileManager defaultManager] URLsForDirectory: NSDesktopDirectory
inDomains: NSUserDomainMask] lastObject];
NSURL *workingdirURL = [desktopURL URLByAppendingPathComponent:#"WorkingDir" isDirectory:YES];
NSArray *docURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:workingdirURL
includingPropertiesForKeys:#[NSURLTypeIdentifierKey] options:NSDirectoryEnumerationSkipsHiddenFiles
error: nil];
NSMutableArray *pdfURLs = [NSMutableArray array];
for (NSURL *docURL in docURLs) {
id resourceValue;
BOOL found = [docURL getResourceValue: &resourceValue
forKey: NSURLTypeIdentifierKey
error: nil];
if ( found && [resourceValue isEqual:#"com.adobe.pdf"]) {
[pdfURLs addObject: docURL];
}
}
You can see the docs for more information about the file properties that can be retrieved this way. There is quite a lot of information available through this method.

AppleScript is cool. Can I do the same thing in plain Objective-C?

so I'm working on my little Mac app, and I want control Adium with it. AppleScript is very cool:
tell application "Adium"
go away with message "Zoned in for Maths."
end tell
(If you're wondering what this is supposed to be. In order to actually start studying I need to create application that will change my IM status, of course ... )
I tried it in Script Editor, it worked, and I'm pretty sure that calling AppleScript from Cocoa application is gonna be trivial.
But.
Is AppleScript the only way? I don't mind using AppleScript, but it looks like programming for noobs.
Is there any way to do the same thing as above code does without AppleScript, in plain Objective-C somehow?
Would someone point me to relevant documentation? I tried Google but it was like I don't even know what I'm looking for.
Thanks!
Interprocess communication in Mac OS X is done by something called Apple Events. AppleScript is one way to send and receive Apple Events to other applications.
Therefore, you just need to construct Apple Events directly and send it to the other app, from Objective-C or whatever other language.
Honestly, if you just want to change the status of Adium, it's easiest to use NSAppleScript and pass what you just wrote, from inside Objective-C.
If you want to do more complicated stuff, Scripting Bridge is the way to go. This mechanism maps Apple Events' object hierarchy to Objective-C's object hierarchy.
If you think that's still a newbie's way, you should directly create Apple Events via NSAppleEventDescriptor.
Well, some of us old timers think using Objective-C is a sissy's way. If you think so, you should directly deal with C structs called AEDesc and such. See Apple Events programming guide and the corresponding reference.
However, I think people who use OS X are all noobs. Real people use Linux.
My dad would say people who use GUI are just too spoiled.
The point is, you don't have to care whether it is a newbie's way or not. The important thing is whether you can achieve what you want. In fact, AppleScript is a very powerful, dynamical language, whose power is not well appreciated by many people. Read AppleScript language guide and be surprised.
The simplest way would be to use NSAppleScript
NSAppleScript *script = [[NSAppleScript alloc]
initWithSource:#"tell application \"Adium\" to go away with message \"Zoned in for Maths.\""
];
For a more powerful way of accessing scripting, use Scripting Bridge. Scripting Bridge requires at least the 10.5 SDK. You first need to prepare your app.
Use the sdef and sdp command line utilities to generating header files for the applications you wish to control (see "Preparing to Code" for details).
Add the generated header to your project.
Add the ScriptingBridge framework to your project. After that, you can use Objective-C calls to control the other application.
After that, you can use Objective-C to send scripting commands to the application.
AdiumApplication *adium = [SBApplication applicationWithBundleIdentifier:#"com.adiumX.adiumX"];
for (AdiumAccount* acct in [adium accounts]) {
[acct goAwayWithMessage:(AdiumRichText *)#"Zoned in for Maths."];
}
Status messages are Adium's rich text type (which is NSTextStorage under the hood), but it's convertable from plain text, so passing an NSString rather than a true AdiumRichText should work fine.
There are a few hoops to jump through. For example, you can't create scripting objects in the target application by using its ObjC classes directly; you must use classForScriptingClass: to get the class, which you can then use to create objects as normal (i.e. alloc and init, initWithProperties &c.).
// creating an AdiumContactGroup
NSDictionary *props = [NSDictionary
dictionaryWithObjectsAndKeys:
#"mathies",#"name",
nil
];
AdiumContactGroup *mathies= [[[[adium classForScriptingClass:#"contact group"] alloc]
initWithProperties:props]
autorelease];
if (mathies) {
[[adium contactGroups] addObject:mathies];
}
Note that other languages (such as Python and Ruby) also have Scripting bindings.
Others have mentioned sending full Applescript or using the Scripting Bridge. A third choice is to use Appscript which is also available for Python and Ruby. It is a little cleaner (IMO) than using the Scripting Bridge in some ways. And definitely easier. Although in other ways Scripting Bridge is better. It also has the advantage of an application called ASTranslate which will translate most Applescript calls into Appscript. Here's the Appscript for your little Applescript example.
ADApplication *adium = [ADApplication applicationWithName: #"Adium"];
ADGoAwayCommand *cmd = [[adium goAway] withMessage: #"Zoned in for Maths."];
id result = [cmd send];