how to convert a path in NSString to CFURLRef and to FSRef* - objective-c

I need to use several functions requiring CFURLRef and FSRef* and for the moment I just have a path stored in an NSString.
What is the (most efficient) way to perform this conversion?
Thanks in advance for your help,

A path can be easily converted to a CFURL by using NSURL, which it is toll-free bridged with. There is also a CFURL function which will give you a FSRef for it. This code will give you both, given an NSString named thePath.
CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:thePath];
FSRef fileRef;
CFURLGetFSRef(url, &fileRef);
If you already have a valid pointer to a FSRef, you can pass it to CFURLGetFSRef directly.

Related

Convert NSValue to NSData and back again, with the correct type

I would like to be able to convert an objective-c object, such as an NSArray or UIImage into an NSData object, which I can then use to write to disk. I first converted them to an NSValue, which I then planned on converting to NSData. This question provided part of the answer, but as they were working with NSNumber, they didn't really have a need to convert it back to NSValue.
I have seen other questions such as this one that relies on NSKeyedArchiver, but I would like to steer away from this due to the vast size inflation that occurs.
Therefore my code at the moment for encoding an NSData object from an NSValue, from the first question, is as follows:
+(NSData*) dataWithValue:(NSValue*)value {
NSUInteger size;
const char* encoding = [value objCType];
NSGetSizeAndAlignment(encoding, &size, NULL);
void* ptr = malloc(size);
[value getValue:ptr];
NSData* data = [NSData dataWithBytes:ptr length:size];
free(ptr);
return data;
}
My question is how would I go about decoding an NSData object that has been encoded in this manner and get back the original objCType from the NSValue?
I would assume I would be using something along these lines
[NSValue valueWithBytes:[data bytes] objCType:typeHere];
How would I get back the type information?
Use NSKeyedArchiver to save the items and NSKeyedUnarchiver to restore the object. The object must conform to NSCoding and in the case of a collection all contained items must also conform to NSCoding.
See the Apple documentation of NSKeyedArchiver and NSCoder
Your approach will only work for primitive types (int, float, structs without pointers, ...) inside your NSValue. Otherwise you will only get the meaningless pointer value but not the actual data in your NSData object.
To also pass the actual type string along you would have to figure out a way to get this inside your NSData object as well. Not impossible, but it will not solve your actual problem.
Using a keyed archiver as zaph suggests is much better.

iOS: Obtaining path of file in c code

I am having an iOS Project in which i use some C-Sources.
In the C part I need the path to a file in the mainbundle.
How can I obtain it without using Obj-C?
Basically I want to have the path returned by:
NSLog(#"%s",[[[NSBundle mainBundle] pathForResource:#"myfile" ofType:#"txt"] fileSystemRepresentation]);
So just get the path as you did above, and then get a C string version:
NSString *path = [[[NSBundle mainBundle] pathForResource:#"myfile" ofType:#"txt"] fileSystemRepresentation];
char *cPath = [path cStringUsingEncoding:UTF8StringEncoding];
Just pay attention to the warning in the docs and copy the string if you plan to hang onto it beyond the end of the method/function that you're in:
The returned C string is guaranteed to be valid only until either the
receiver is freed, or until the current autorelease pool is emptied,
whichever occurs first. You should copy the C string or use
getCString:maxLength:encoding: if it needs to store the C string
beyond this time.
Update: If for whatever reason you can't use Foundation, you can do something similar using Core Foundation. You can call CFBundleCopyResourceURL() (or one of its cousins) to get the URL for the resource, and then convert that to a path using CFURLCopyPath().
Best you read the path with objective-c and pass it as char * to the c class by calling a function.
You further could asign a global variable char * which is visible for both objective-c and C.

In my code how to launch application responsible for an UTI

My Mac OS X application receives a file over the network (in this case, text/x-vcard). In my code, how can I open the related application (typically the Address Book) without hard-coding paths or application name so that it processes the file ?
You'll be able to do this by linking in the ApplicationServices framework, which has a really handy "LSCopyApplicationForMIMEType" function. It works like this:
CFURLRef appURL = nil;
OSStatus err = LSCopyApplicationForMIMEType(CFSTR("text/x-vcard"), kLSRolesAll, &appURL);
if (err != kLSApplicationNotFoundErr) {
NSLog(#"URL: %#", (NSURL *)appURL);
}
CFRelease(appURL);
I'll explain what the parameters mean. The first parameter is a CFStringRef of the MIME type you're looking up. The second parameter indicates what kind of application you're looking for, ie an app that can edit this file, or an app that can view this file, etc. kLSRolesAll means you don't care. The final parameter is a pointer to the CFURLRef where the function will stick the app's URL (if it can find one).
On my machine, this prints out:
2009-08-01 12:38:58.159 EmptyFoundation[33121:a0f] URL: file://localhost/Applications/Address%20Book.app/
One of the cool things about CFURLRefs is that they're toll-free bridged to NSURL. This means you can take a CFURLRef and cast it to an NSURL, and vice versa. Once you've got your NSURL of the app, it's pretty trivial to use something like NSWorkspace's -launchApplicationAtURL:options:configuration:error: method to open the application.
If you want to open a specific file in that application (like the file from which you got the MIME type), you could use something like -[NSWorkspace openFile:withApplication:].
If you can't get the MIME type (despite what you say in your question), there are a bunch of similar LaunchServices functions. You can read all about them here.
Rather than even bothering to try to find the application you can use LSOpenItemsWithRole.
//Opens items specified as an array of values of type FSRef with a specified role.
OSStatus LSOpenItemsWithRole (
const FSRef *inItems,
CFIndex inItemCount,
LSRolesMask inRole,
const AEKeyDesc *inAEParam,
const LSApplicationParameters *inAppParams,
ProcessSerialNumber *outPSNs,
CFIndex inMaxPSNCount
);

Objective-C: Extract filename from path string

When I have NSString with /Users/user/Projects/thefile.ext I want to extract thefile with Objective-C methods.
What is the easiest way to do that?
Taken from the NSString reference, you can use :
NSString *theFileName = [[string lastPathComponent] stringByDeletingPathExtension];
The lastPathComponent call will return thefile.ext, and the stringByDeletingPathExtension will remove the extension suffix from the end.
If you're displaying a user-readable file name, you do not want to use lastPathComponent. Instead, pass the full path to NSFileManager's displayNameAtPath: method. This basically does does the same thing, only it correctly localizes the file name and removes the extension based on the user's preferences.
At the risk of being years late and off topic - and notwithstanding #Marc's excellent insight, in Swift it looks like:
let basename = NSURL(string: "path/to/file.ext")?.URLByDeletingPathExtension?.lastPathComponent

Unfamiliar C syntax in Objective-C context

I am coming to Objective-C from C# without any intermediate knowledge of C. (Yes, yes, I will need to learn C at some point and I fully intend to.) In Apple's Certificate, Key, and Trust Services Programming Guide, there is the following code:
static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";
I have an NSString that I would like to use as an identifier here and for the life of me I can't figure out how to get that into this data structure. Searching through Google has been fruitless also. I looked at the NSString Class Reference and looked at the UTF8String and getCharacters methods but I couldn't get the product into the structure.
What's the simple, easy trick I'm missing?
Those are C strings: Arrays (not NSArrays, but C arrays) of characters. The last character is a NUL, with the numeric value 0.
“UInt8” is the CoreServices name for an unsigned octet, which (on Mac OS X) is the same as an unsigned char.
static means that the array is specific to this file (if it's in file scope) or persists across function calls (if it's inside a method or function body).
const means just what you'd guess: You cannot change the characters in these arrays.
\0 is a NUL, but including it explicitly in a "" literal as shown in those examples is redundant. A "" literal (without the #) is NUL-terminated anyway.
C doesn't specify an encoding. On Mac OS X, it's generally something ASCII-compatible, usually UTF-8.
To convert an NSString to a C-string, use UTF8String or cStringUsingEncoding:. To have the NSString extract the C string into a buffer, use getCString:maxLength:encoding:.
I think some people are missing the point here. Everyone has explained the two constant arrays that are being set up for the tags, but if you want to use an NSString, you can simply add it to the attribute dictionary as-is. You don't have to convert it to anything. For example:
NSString *publicTag = #"com.apple.sample.publickey";
NSString *privateTag = #"com.apple.sample.privatekey";
The rest of the example stays exactly the same. In this case, there is no need for the C string literals at all.
Obtaining a char* (C string) from an NSString isn't the tricky part. (BTW, I'd also suggest UTF8String, it's much simpler.) The Apple-supplied code works because it's assigning a C string literal to the static const array variables. Assigning the result of a function or method call to a const will probably not work.
I recently answered an SO question about defining a constant in Objective-C, which should help your situation. You may have to compromise by getting rid of the const modifier. If it's declared static, you at least know that nobody outside the compilation unit where it's declared can reference it, so just make sure you don't let a reference to it "escape" such that other code could modify it via a pointer, etc.
However, as #Jason points out, you may not even need to convert it to a char* at all. The sample code creates an NSData object for each of these strings. You could just do something like this within the code (replacing steps 1 and 3):
NSData* publicTag = [#"com.apple.sample.publickey" dataUsingEncoding:NSUnicodeStringEncoding];
NSData* privateTag = [#"com.apple.sample.privatekey" dataUsingEncoding:NSUnicodeStringEncoding];
That sure seems easier to me than dealing with the C arrays if you already have an NSString.
try this
NSString *newString = #"This is a test string.";
char *theString;
theString = [newString cStringWithEncoding:[NSString defaultCStringEncoding]];