Referencing file on disk from NSManagedObject - iphone

What would be the best way to name a file associated to a NSManagedObject. The NSManagedObject will hold the URL to this file.
But I need to create a unique filename for my file. Is there some kind of autoincrement id that I could use? Should I use mktemp (but it's not a temporary file) or try to convert the NSManagedObjectId to a filename? but I fear there will be special characters which might cause problem.
What would you suggest?
EDIT: I have a lot of these NSManagedObjects and each has its own image, so I want to generate a unique name for each picture.

You can use NSProcessInfo to generated the guid:
[[NSProcessInfo processInfo] globallyUniqueString]
And to reference a file I'd suggest just keeping the guid as NSManagedObject property and then just reference a file by that name from application support directory.

There is a good way to do this and one earlier answer almost had it – generate a GUID for each image instead of for the entire process. Call this method whenever you need a unique string, and then store it in the managed object:
+ (NSString *)getUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
I use this for storing captured movies and images.

I thought about using the time to generate a unique filename but I don't like the solution, for instance if the time is reset or changed, there is a slight chance of getting two times the same filename.
I'm surprised not to find more information about this subject on the web.

Since iOS5 there is now the option to store large blobs in an external record file. CoreData decides whether or not to store the record in sqlite based on the size of the record.
Storing blobs in external location using built-in CoreData option

Related

Globally unique random name for Images

I am trying to write code for generating globally unique name for images that are to be uploaded by users from iOS app to server. The names should be randomly generated and should be unique so that the images are not overwritten/replaced.
Here's my code for generating random and unique strings:
+ (NSString *)generateRandNameWithLength:(int)len
{
NSString *letters = [NSString stringWithFormat:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#0123456789", [HJUtilities generateUniqueApId]];
NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
for (int i=0; i<len; i++) {
[randomString appendFormat: #"%C", [letters characterAtIndex: arc4random() % [letters length]]];
}
return randomString;
}
Where:
+ (NSString *)generateUniqueApId
{
NSString *appId = (__bridge NSString *) CFUUIDCreateString (NULL, CFUUIDCreate(NULL));
return appId;
}
returns a UUID.
Now I'm not sure whether this is the correct code for generating globally unique strings. I don't know how to verify this to be certain that no user will overwrite another user's image.
Note: I'm using Amazon Web services for storage. And one common bucket will be used for all the images of all users. So, its required that images names should be unique.
There is no need for the code you have. All you need is the CFUUIDCreateString function. This will be unique across all users on all devices.
From the docs for CFUUID:
UUIDs (Universally Unique Identifiers), also known as GUIDs (Globally Unique Identifiers) or IIDs (Interface Identifiers), are 128-bit values guaranteed to be unique. A UUID is made unique over both space and time by combining a value unique to the computer on which it was generated—usually the Ethernet hardware address—and a value representing the number of 100-nanosecond intervals since October 15, 1582 at 00:00:00.
The code you have now is definitely not guaranteed to be unique.
There is a new class added in iOS 6.0
#interface NSUUID : NSObject <NSCopying, NSSecureCoding>
To simplify the memory management you for sure can use it
You're overcomplicating it. Just get a UUID using CFUUIDCreateString and use that string. Adding extra layers of randomness isn't going to help. In fact, it's probably going to make things worse by increasing the chance of a name collision.
The only argument against using a UUID directly is that it may be possible to identify the source of the upload, since the UUID is (or can be) generated using device's MAC address, which is specific to the hardware. You won't be able to identify a user with nothing but a UUID, but you would be able to say "this collection of UUIDs came from the same device" or "this UUID came from this device". (See RFC 4122 for the various UUID formats and a discussion of this issue in section 4.5.)
If anonymity is a concern, running the UUID through a hash function, like SHA1 or MD5, would be good enough to make it unidentifiable.
Using a loop where you "add randomness" by mixing in random numbers is like shaking a dice for a few seconds versus an hour: the only difference is that you're rubbing sweat onto the dice.
As far as a locally unique string — which you could use as part of the file name — this is handy:
[[NSProcessInfo processInfo] globallyUniqueString]

How to insert data from NSArrays in to coredata

I have an NSArray called namesArray.
I need to store all the names existing in namesArray using coredata.
How do i achieve this ?
Does we need to create any database like 'names.sqlite' using sqlite manager?
No you don't need to create a names.sqlite using manager. You should go through some of the tutorial found on the net for example: Here or Here.
You basically need to save in the datamodel in valueForKey format.
//Coredata saving
self.theAppDel=[[UIApplication sharedApplication] delegate];
NSManagedObjectContext*context=[self.theAppDel managedObjectContext];
NSManagedObject*object=[NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
NSError*error=nil;
[object setValue:[namesArray objectAtIndex:0] forKey:#"name"]; //for saving first name
1. Creating an instance of your appdelegate .
2. Access your Managed Object Context and Managed Object
3. Assuming you created a entity description with name contacts. With the count of number of items in your array, add each object for a column named name (which i'm assuming you would have created).
This is short example, but you should go through the tutorials and read apple's documentation.
EDIT: As Ondra mentioned my earlier solution would have added only the last element. Use the following for adding: Adding NSMutableArray in CoreData Thanks Ondra Peterka
I think that iNoobs answer would not work - the loop would overwrite the value several times (only last name would be saved). In every case reading the tutorials is good idea.
Maybe the answer you are looking for is here: Saving an NSMutableArray to Core Data
Also ... I know you explicitly said "save to core data", but just in case - you can use also different storage like NSUserDefaults:
[[NSUserDefaults standardUserDefaults] setObject:namesArray forKey:#"namesArray"];
and retrieve it later using:
myArray= [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"namesArray"]];
Of course NSUserDefaults are ment to be used only for small amount of data.
Good luck ;)

Loading text from a file

I am making an Iphone drinking card game app.
All the card mean something different and i want the user to be able to press an info button and then show a new screen with information about the current card. How can i make a document to load text from instead of using a bunch og long strings?
Thanks
You could look into plist files - they can be loaded quite easily into the various collection objects and edited with the plist editor in Xcode.
For instance, if you organize your data as a dictionary, the convenience constructor
+ (id)dictionaryWithContentsOfURL:(NSURL *)aURL
from NSDictionary would provide you with as many easily accessible strings as you need.
This method is useful if you consider your strings primarily data as opposed to UI elements.
Update:
As #Alex Nichol suggested, here is how you can do it in practice:
To create a plist file:
In your Xcode project, for instance in the Supporting Files group, select New File > Resource > Property List
You can save the file in en.lproj, to aid in localization
In the Property list editing pane, select Add Row (or just hit return)
Enter a key name (for instance user1) and a value (for instance "Joe")
To read the contents:
NSURL *plistURL = [[NSBundle mainBundle] URLForResource:#"Property List" withExtension:#"plist"];
NSLog(#"URL: %#", plistURL);
NSDictionary *strings = [NSDictionary dictionaryWithContentsOfURL:plistURL];
NSString *user1 = [strings objectForKey:#"user1"];
NSLog(#"User 1: %#", user1);
A plist, a JSON string, and an SQLite database walked into a bar ...
Oops!! I mean those are the three most obvious alternatives. The JSON string is probably the easiest to create and "transport", though it's most practical to load the entire thing into an NSDictionary and/or NSArray, vs read from the file as each string is accessed.
The SQLite DB is the most general, and most speed/storage efficient for a very large number (thousands) of strings, but it takes some effort to set it up.
In my other answer, I suggest the use of a dictionary if your texts are mostly to be considered as data. However, if your strings are UI elements (alert texts, window titles, etc.) you might want to look into strings files and NSBundle's support for them.
Strings files are ideally suited for localization, the format is explained here.
To read them into you app, use something like this:
NSString *text1 = NSLocalizedStringFromTable(#"TEXT1", #"myStringsFile", #"Comment");
If you call your file Localizable.strings, you can even use a simpler form:
NSString *str1 = NSLocalizedString(#"String1", #"Comment on String1");
A useful discussion here - a bit old, but still useful.

How to use ManagedObjectID the right way?

What I'm trying is this:
1) Create a new manged object
2) Get it's temporary id with [myMO objectID];
3) Convert that ID to an NSURL, so I can save it for future reference:
NSManagedObjectID *moID = [myMO objectID];
NSURL *url = [moID URIRepresentation];
4) Save the managed object context
5) Some time later, fetch that object using the NSURL as ID
NSManagedObjectID *moID = [[context persistentStoreCoordinator] managedObjectIDForURIRepresentation:url];
And guess what: It does not work. I get an empty-stupid object back from
NSManagedObject *myOldMo = [context existingObjectWithID: moID error:&error];
But...as I said...the ID is temporary when creating an managed object. So it does make sense why this doesn't work at all. I must first save the context, and then I get a persistet ID. The real one. Right?
So is that the way to go?
1) Create the managed object
2) Save the context
3) Get the ID as NSURL
4) any time later, for example on your next birthday, access the managed object with the NSURL ;-)
I try to dream of NSManagedObjectID like a DB id which I can write on some yellow postIt sheet and glue on the middle of my monitor, so I refer back to it after lunch. You know... at least like in the old days where we used databases over telnet and executed SQL commands manually to query order information and stuff like that. The ID was the most important and significant thing, all the time.
But Core Data has this somewhat strange NSManagedObjectID thing.
What are your secret strategies? Do you actually recognize many use cases where you would need that NSManagedObjectID? Or is that something I could easily forget with no pain afterwards?
I'm not sure that it's such a big secret. The documentation describes the way to get permanent IDs for managed objects from the NSManagedObjectContext:
- (BOOL)obtainPermanentIDsForObjects:(NSArray *)objects error:(NSError **)error
http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html#//apple_ref/occ/instm/NSManagedObjectContext/obtainPermanentIDsForObjects:error:

iPhone: Fastest way to create a binary Plist with simple key/value strings

What's the best way to create a binary plist on the iPhone with simple string based key/value pairs? I need to create a plist with a list of recipe and ingredients. I then want to be able to read this into an NSDictionary so I can do something like
NSString *ingredients = [recipes objectForKey:#"apple pie"];
I'm reading in an XML data file through an HTTP request and want to parse all of the key value pairs into the plist. The XML might look something like:
<recipes>
<recipe>
<name>apple pie</name>
<ingredients>apples and pie</ingredients>
</recipe>
<recipe>
<name>cereal</name>
<ingredients>milk and some other ingredients</ingredients>
</recipe>
</recipes>
Ideally, I'll be able to write this to a plist at runtime, and then be able to read it and turn it into an NSDictionary later at runtime as well.
Creating a Property List in Objective-C
This contains information for creating a property list file using either CoreFoundation or Cocoa.
For your specific case, look at the interface for NSDictionary and use:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
+ (id)dictionaryWithContentsOfFile:(NSString *)path;
If you only need one fairly small dictionary, you could also try NSUserDefaults.
- (void)setObject:(id)value forKey:(NSString *)defaultName;
- (NSDictionary *)dictionaryForKey:(NSString *)defaultName;