Inputstring is null after being set in a static method, why? - objective-c

I am uncertain of how memory is managed in my particular case...
I have two methods:
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
plistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:plistPath];
NSLog(#"AFTER plistPath: \n%#",plistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableString * plistPath;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",plistPath);
// Write to plist
bool res = [temp writeToFile:plistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}
The problem is that the bottom method that sends in "plistPath" to the above method retrives a null plistPath after the above method has set it. Why and how can I fix this?
NSLog(#"path: %#",plistPath);
in the bottom method shows null, why?
I use ARC. Also "destPath" is set and shows the correct path.

I believe you could be a bit confused here.
You are creating plistPath in the bottom method. And then you pass plistPath into
[BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
but plistPath is NULL
NSMutableString * plistPath; // Is NULL
But once it has been passed in the local plistPath takes over.
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath // Notice the local plistPath variable. This is the one you are playing with in this method.
At this point you are setting plistPath but remember it is still just a local variable and not an instance variable. So the button method will never know about it being set, as far as the button method is concerned it is still NULL
plistPath = [NSMutableString stringWithString:[destPath copy]];
So whatever you set in plistPath in the top method will not get passed back to the bottom method, think of the top plistPath as being deallocated when the method does the return.
So the plistPath in the bottom method will remain NULL
So try this instead SOLUTION
static NSMutableString *yourNewStringforPlistPath; //This will be NULL
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
yourNewStringforPlistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:yourNewStringforPlistPath];
NSLog(#"AFTER plistPath: \n%#",yourNewStringforPlistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:[NSNull null]]; // As this is already NULL you don't really need to pass yourNewStringforPlistPath in unless in the future this value can be set before this.
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",yourNewStringforPlistPath);
// Write to plist
bool res = [temp writeToFile:yourNewStringforPlistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}

Related

NSMutableDictionary not saving to local directory

I test to see if a directory is saving and it doesn't seem that it is.
Here is what I have, it's essentially creating a list of which files have been uploaded to Dropbox. DropboxList is declared in the header file and then synchronized.
int i = 0;
DropboxList = [[NSMutableDictionary alloc]init];
do {
[DropboxList setObject:#"NO" forKey:[NSNumber numberWithInt:i]];
i++;
} while (i < sortedFiles.count);
NSLog(#"dropbox list is %#", DropboxList);
NSArray *dropBoxPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dropBoxDocumentsDirectory = [dropBoxPaths objectAtIndex:0];
NSString *dropBoxDataPath = [dropBoxDocumentsDirectory stringByAppendingPathComponent:#"/DropboxUploads"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dropBoxDataPath]) {
[[NSFileManager defaultManager] createDirectoryAtPath:dropBoxDataPath withIntermediateDirectories:NO attributes:nil error:nil];
}
NSString *filePath = [dropBoxDataPath stringByAppendingPathComponent:#"dropboxList.out"];
[DropboxList writeToFile:filePath atomically:YES];
NSMutableDictionary *newDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
NSLog(#"newDictionary list is %#", newDictionary);
Here is my console:
dropbox list is {
0 = NO;
3 = NO;
2 = NO;
1 = NO;
4 = NO;
}
newDictionary list is (null)
I've tried alloc init for the newDictionary before writing and I get the same result. Any ideas?
The problem is with the following line
[DropboxList setObject:#"NO" forKey:[NSNumber numberWithInt:i]];
DropboxList writeToFile only works with the valid property list. For a NSMutableDictionary to be a valid property list object, its keys should be string type instead of NSNumber.
Solution
[DropboxList setObject:#"NO" forKey:[NSString stringWithFormat:#"%d",i]]];
Also, make use of the writeToFile return parameter to validate.
BOOL isWritten = [DropboxList writeToFile:filePath atomically:YES];
if (isWritten)
{
NSMutableDictionary *newDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
NSLog(#"newDictionary list is %#", newDictionary);
{
else
{
NSLog(#"Something went wrong");
}
Okay, so my problem was that I had NSNumbers as the keys, which doesn't work. I made the keys strings, and we're golden.

Cannot append to NSString

I'm trying to append the file extension to a the stringValue returned by a subclassed NSTextFieldCell
I've tried everything I knew and could find on the internet, but this is just giving me a headache
the method is the following:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSMutableString *filename = [[NSMutableString alloc] init];
[filename appendString:self.stringValue];
NSString *iconFileName = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:filename] stringByAppendingPathExtension:#"png"];
NSLog(#"%#", iconFileName);
}
The returned value is without the extension though!
I've also tried the following:
filename = [NSString stringWithFormat: #"%#.png", filename];
This returns the "filename" string without the ".png"
Similarly:
filename = [filename stringByAppendingString: #".png"];
returns just the "filename"
The table column where this cell belongs to is bound to an NSObject, and the method that sends the data to the column is the following:
- (NSString *) nationString {
NSMutableString *string = [[NSMutableString alloc] init];
int index = 0;
if (nationAddress && nationAddress > 0x0) {
index = [[[[controller database] nationsAddressIndex] valueForKey:[NSString stringWithFormat:#"%lli", nationAddress]] intValue];
Nation *nationality = [[[controller database] nations] objectAtIndex:index];
[string appendString:[nationality name]];
}
else {
[string appendString:#"---"];
}
return string;
}
Anyone has any idea why this might be happening, or can suggest any alternatives?
Any help will be appreciated
Thanks
This should return the complete path with extension:
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Default.png"];
NSLog(#"%#", path);
So, assuming self.stringValue includes the extension, your method should work with this:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSString *iconFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.stringValue];
NSLog(#"%#", iconFileName);
}
If it doesn't include the extension, try this:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSString *strWithPath = [NSString stringWithFormat:#"%#.png", self.stringValue];
NSString *iconFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:strWithPath];
NSLog(#"%#", iconFileName);
}
Just for test. Try to use this code and update here a output values:
NSMutableString *filename = [[NSMutableString alloc] init];
[filename appendString:self.stringValue];
NSLog(#"text1: %# ;", filename);
filename = [NSString stringWithFormat: #"%#.png", filename];
filename = [NSString stringWithFormat: #"%#.png%#", filename, filename];
NSLog(#"text2: %# ;", filename);
These should work (barring a typo):
NSString* filename = #"abc";
NSString* result1 = [NSString stringWithFormat:#"%#.png", filename];
NSString* result2 = [filename stringByAppendingString:#".png"];
NSMutableString* result3 = [filename mutableCopy];
[result3 appendString:#".png"];
If they don't appear to be working then you have some problem with how you're initializing or displaying your values.
Hint: Place an NSLog(#"The answer is %#", resultN); statement immediately after each of the above (with "resultN" changed appropriately) to see what you're getting. Keep in mind that if you look from a different object you may be looking at different variables.

NSString unique file path to avoid name collisions

Is there a simple way to take a given file path and modify it in order to avoid name collisions? Something like:
[StringUtils stringToAvoidNameCollisionForPath:path];
that for a given path of type: /foo/bar/file.png, will return /foo/bar/file-1.png and later it will increment that "-1" similarly to what Safari does for downloaded files.
UPDATE:
I followed Ash Furrow's suggestion and I posted my implementation as answer :)
I had a similar problem, and came up with a slightly broader approach, that attempts to name files the same way iTunes would (when you have it set to manage your library and you have multiple tracks with the same name, etc.)
It works in a loop, so the function can be called multiple times and still produce valid output. Explaining the arguments, fileName is the name of the file with no path or extension (e.g. "file"), folder is just the path (e.g. "/foo/bar"), and fileType is just the extension (e.g. "png"). These three could be passed in as one string and be split out after, but in my case it made sense to separate them.
currentPath (which can be empty, but not nil), is useful when you're renaming a file, not creating a new one. For example, if you have "/foo/bar/file 1.png" that you're trying to rename to "/foo/bar/file.png", you would pass in "/foo/bar/file 1.png" for currentPath, and if "/foo/bar/file.png" already exists, you'll get back the path you started with, instead of seeing that "/foo/bar/file 1.png" and returning "/foo/bar/file 2.png"
+ (NSString *)uniqueFile:(NSString *)fileName
inFolder:(NSString *)folder
withExtension:(NSString *)fileType
mayDuplicatePath:(NSString *)currentPath
{
NSUInteger existingCount = 0;
NSString *result;
NSFileManager *manager = [NSFileManager defaultManager];
do {
NSString *format = existingCount > 0 ? #"%# %lu" : #"%#";
fileName = [NSString stringWithFormat:format, fileName, existingCount++];
result = [fileName stringByAppendingFormat:#".%#", [fileType lowercaseString]];
result = [folder stringByAppendingPathComponent:result];
} while ([manager fileExistsAtPath:result] &&
// This comparison must be case insensitive, as the file system is most likely so
[result caseInsensitiveCompare:currentPath] != NSOrderedSame);
return result;
}
I decided to implement my own solution and I want to share my code. It's not the most desirable implementation, but it seems to do the job:
+ (NSString *)stringToAvoidNameCollisionForPath:(NSString *)path {
// raise an exception for invalid paths
if (path == nil || [path length] == 0) {
[NSException raise:#"DMStringUtilsException" format:#"Invalid path"];
}
NSFileManager *manager = [[[NSFileManager alloc] init] autorelease];
BOOL isDirectory;
// file does not exist, so the path doesn't need to change
if (![manager fileExistsAtPath:path isDirectory:&isDirectory]) {
return path;
}
NSString *lastComponent = [path lastPathComponent];
NSString *fileName = isDirectory ? lastComponent : [lastComponent stringByDeletingPathExtension];
NSString *ext = isDirectory ? #"" : [NSString stringWithFormat:#".%#", [path pathExtension]];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"-([0-9]{1,})$" options:0 error:nil];
NSArray *matches = [regex matchesInString:fileName options:0 range:STRING_RANGE(fileName)];
// missing suffix... start from 1 (foo-1.ext)
if ([matches count] == 0) {
return [NSString stringWithFormat:#"%#-1%#", fileName, ext];
}
// get last match (theoretically the only one due to "$" in the regex)
NSTextCheckingResult *result = (NSTextCheckingResult *)[matches lastObject];
// extract suffix value
NSUInteger counterValue = [[fileName substringWithRange:[result rangeAtIndex:1]] integerValue];
// remove old suffix from the string
NSString *fileNameNoSuffix = [fileName stringByReplacingCharactersInRange:[result rangeAtIndex:0] withString:#""];
// return the path with the incremented counter suffix
return [NSString stringWithFormat:#"%#-%i%#", fileNameNoSuffix, counterValue + 1, ext];
}
... and the following are the tests I used:
- (void)testStringToAvoidNameCollisionForPath {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
// bad configs //
STAssertThrows([DMStringUtils stringToAvoidNameCollisionForPath:nil], nil);
STAssertThrows([DMStringUtils stringToAvoidNameCollisionForPath:#""], nil);
// files //
NSString *path = [bundle pathForResource:#"bar-0.abc" ofType:#"txt"];
NSString *savePath = [DMStringUtils stringToAvoidNameCollisionForPath:path];
STAssertEqualObjects([savePath lastPathComponent], #"bar-0.abc-1.txt", nil);
NSString *path1 = [bundle pathForResource:#"bar1" ofType:#"txt"];
NSString *savePath1 = [DMStringUtils stringToAvoidNameCollisionForPath:path1];
STAssertEqualObjects([savePath1 lastPathComponent], #"bar1-1.txt", nil);
NSString *path2 = [bundle pathForResource:#"bar51.foo.yeah1" ofType:#"txt"];
NSString *savePath2 = [DMStringUtils stringToAvoidNameCollisionForPath:path2];
STAssertEqualObjects([savePath2 lastPathComponent], #"bar51.foo.yeah1-1.txt", nil);
NSString *path3 = [path1 stringByDeletingLastPathComponent];
NSString *savePath3 = [DMStringUtils stringToAvoidNameCollisionForPath:[path3 stringByAppendingPathComponent:#"xxx.zip"]];
STAssertEqualObjects([savePath3 lastPathComponent], #"xxx.zip", nil);
NSString *path4 = [bundle pathForResource:#"foo.bar1-1-2-3-4" ofType:#"txt"];
NSString *savePath4 = [DMStringUtils stringToAvoidNameCollisionForPath:path4];
STAssertEqualObjects([savePath4 lastPathComponent], #"foo.bar1-1-2-3-5.txt", nil);
NSString *path5 = [bundle pathForResource:#"bar1-1" ofType:#"txt"];
NSString *savePath5 = [DMStringUtils stringToAvoidNameCollisionForPath:path5];
STAssertEqualObjects([savePath5 lastPathComponent], #"bar1-2.txt", nil);
// folders //
NSString *path6 = [DOCUMENTS_PATH stringByAppendingPathComponent:#"foo1"];
NSString *savePath6 = [DMStringUtils stringToAvoidNameCollisionForPath:path6];
STAssertEqualObjects([savePath6 lastPathComponent], #"foo1-1", nil);
NSString *path7 = [DOCUMENTS_PATH stringByAppendingPathComponent:#"bar1-1"];
NSString *savePath7 = [DMStringUtils stringToAvoidNameCollisionForPath:path7];
STAssertEqualObjects([savePath7 lastPathComponent], #"bar1-2", nil);
NSString *path8 = [DOCUMENTS_PATH stringByAppendingPathComponent:#"foo-5.bar123"];
NSString *savePath8 = [DMStringUtils stringToAvoidNameCollisionForPath:path8];
STAssertEqualObjects([savePath8 lastPathComponent], #"foo-5.bar123-1", nil);
}

Add data to plist

I want to create a plist in code and add more "records" from user input to it. I have created a plist, and I can write the user input, but I can't add data to my plist. Every time, it saves the last input and keeps only one "record". This is the code:
-(void) createPlistFile {
manager = [NSFileManager defaultManager];
filepath = #"/Users/agnostos_el/Downloads/PhoneBook/PhoneBook/Epafes.txt";
if ( [manager fileExistsAtPath:filepath] == NO )
{
NSLog(#"Το Αρχειο Δεν Υπαρχει Το δημειουργω");
[[NSFileManager defaultManager]createFileAtPath:filepath contents:nil attributes:nil];
}
}
-(void) openPlistFile {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if( [path count] > 0 ){
pListFile = [[path objectAtIndex:0] stringByAppendingPathComponent:filepath];
NSDictionary *pListDict = [[NSDictionary alloc]initWithContentsOfFile:filepath];
if ( [pListDict count] <= 0 )
{
}
[self createPlistFile];
}
}
- (void) times
{
filepath = #"/Users/agnostos_el/Downloads/PhoneBook/PhoneBook/Epafes.txt";
stoixeia = [NSArray arrayWithObjects:onoma.stringValue, poli.stringValue, odos.stringValue, per.stringValue, tk.stringValue, xora.stringValue,kin.stringValue, kin1.stringValue, kin2.stringValue, kat.stringValue, erg.stringValue, fax.stringValue, email.stringValue, email1.stringValue, email2.stringValue, pros.stringValue, sim.stringValue, nil];
eponimos = epitheto.stringValue;
NSMutableDictionary *atomo = [NSMutableDictionary dictionary];
innerDict = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects: eponimos, stoixeia, nil]
forKeys:[NSArray arrayWithObjects:#"eponimo", #"stoixeia", nil]];
[atomo setObject:innerDict forKey:eponimos];
id plist = [NSPropertyListSerialization dataFromPropertyList:(id)atomo
format:NSPropertyListXMLFormat_v1_0 errorDescription:nil];
record = [[NSData alloc]initWithData:plist];
[record writeToFile:filepath atomically:YES];
}
- (void) eggrafi
{
[self openPlistFile];
[self times];
}
Any suggestions? Thanks.
You are not passing the file contents to createFileAtPath:contents:attributes::
[[NSFileManager defaultManager]createFileAtPath:filepath contents:nil attributes:nil];

Check the attribute of items in a directory in Objective-C

I have made this little code to check how many subdirectories are in a given directory. It checks only the first level, is there anyway I can make it simplier? I have added comments, maybe easier to understand my intention. Thank you!
#import < Foundation/Foundation.h >
int main (int argc, const char * argv[]){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSFileManager *filemgr;
NSMutableArray *listOfFiles;
NSDictionary *listOfFolders;
NSDictionary *controllDir;
int i, count;
NSString *myPath;
filemgr = [NSFileManager defaultManager];
myPath = #"/";
// list the files in the given directory (myPath)
listOfFiles = [filemgr directoryContentsAtPath: myPath];
// count the number of elements in the array
count = [listOfFiles count];
// check them one by one
for (i = 0; i < count; i++)
{
// I need the full path
NSString *filePath =[NSString stringWithFormat:#"%#/%#", myPath, [listOfFiles objectAtIndex: i]];
// add every item with its attributes
listOfFolders = [filemgr attributesOfItemAtPath:filePath error:NULL];
// to avoid typo get the attribute and create a string
controllDir = [filemgr attributesOfItemAtPath:#"/" error:NULL];
NSString *toCheck = [NSString stringWithFormat:#"%#", [controllDir objectForKey:NSFileType]];
// the folder elements one by one
NSString *fileType = [NSString stringWithFormat:#"%#", [listOfFolders objectForKey:NSFileType]];
if([toCheck isEqualToString:fileType])
{
NSLog(#"NAME: %# TYPE: %#" ,[listOfFiles objectAtIndex:i],[listOfFolders objectForKey:NSFileType]);
}
}
[pool drain];
return 0;
}
NSURL *url = [NSURL fileURLWithPath:#"/Users"];
NSError *error;
NSArray *items = [[NSFileManager defaultManager]
contentsOfDirectoryAtURL:url
includingPropertiesForKeys:[NSArray array]
options:0
error:&error];
NSMutableArray *dirs = [NSMutableArray array];
for (NSURL *url in items) {
if (CFURLHasDirectoryPath((CFURLRef)url)) {
[dirs addObject:url];
}
}
}
You can get fancy with blocks this way:
NSPredicate *predicate = [NSPredicate predicateWithBlock:
^BOOL (id evaluatedObject, NSDictionary *bindings){
return CFURLHasDirectoryPath((CFURLRef)evaluatedObject); }];
NSArray *dirs = [items filteredArrayUsingPredicate:predicate]);
Of note here is that it only hits the disk the one time, and it doesn't spend any time fetching unneeded attributes. Once you construct the NSURL, you can always tell if it's a directory because it ends in a / (this is specified behavior). That's all CFURLHasDirectoryPath() is doing. It doesn't actually hit the disk.
Brief thoughts (posting from a cell phone):
use an NSDirectoryEnumerator.
it has a method called fileAttributes that will return an NSDictionary with the item's attributes.
NSDictionary has a fileType method that will return a constant to indicate the kind of the item.
there's a nice constant called NSFileTypeDirectory you can use for comparison.
How's this?
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error;
NSArray *subpaths = [fm contentsOfDirectoryAtPath:myPath error:&error];
if (!subpaths) ...handle error.
NSMutableArray *subdirs = [NSMutableArray array];
for (NSString *name in subpaths)
{
NSString *subpath = [myPath stringByAppendingPathComponent:name];
BOOL isDir;
if ([fm fileExistsAtPath:subpath isDirectory:&isDir] &&
isDir)
{
[subdirs addObject:subpath];
}
}
Now the subdirs array contains all of the immediate subdirectories.