I am currently working on having a file update from a remote server. I am able to download the file and save it to the documents directory. The file has a "Last-Modified" tag and I am using it to check if the file needs to be updated. But my question is, how do you save the string with the tag for later use? Later I want to compare the saved string with the another string with the current "Last-Modified" tag. If they are equal the file doesn't have to be updated but if they're not equal I will download the new file.
Sorry for bad English, correct me and any help is appreciated. Have been struggling with this for a while!
EDIT:
NSDictionary *metaData = [test allHeaderFields];
//NSLog(#"%#", [metaData description]);
lastModifiedString = [metaData objectForKey:#"Last-Modified"];
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setObject:lastModifiedString forKey:#"LastModified"];
[standardUserDefaults synchronize];
NSString *savedString = [[NSUserDefaults standardUserDefaults] stringForKey:#"LastModified"];
if (![lastModifiedString isEqualToString:savedString])
{
[self downloadNewFile];
}
Download link to files: Archive.zip
Use NSUserDefaults or Core Data to persist a value.
EDIT:
It is not working because you are saving the new value before retrieving it. You'll need to move
NSString *savedString = [[NSUserDefaults standardUserDefaults] stringForKey:#"LastModified"];
above
[standardUserDefaults setObject:lastModifiedString forKey:#"LastModified"];
Now you'll be comparing the new file value against the old user defaults value.
You are saying you want to compare the Last Modified dates to see if they are the same?
I think the best way to do this would be to save the date (as a string) in a Property List. If you are making an iPhone app you can create a property list with a string with the code below. This code checks if a file already exists and if it does, it reads from it, and if it doesn't, it creates one and writes to it.
// Get the path to the property list.
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *pathToPlist = [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"yourfilename.plist"];
// Check whether there is a plist file that already exists.
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:pathToPlist];
if (!fileExists) {
// There is no file so we set the file up and save it.
NSMutableDictionary *newDict = [NSMutableDictionary dictionaryWithCapacity:1];
[newDict setObject:yourStringWithTheLastModifiedDate forKey:#"lastModified"];
[newDict writeToFile:pathToPlist atomically:YES];
}
} else {
// There is already a plist file. You could add code here to write to the file rather than read from it.
// Check the value of lastModified.
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:pathToPlist];
NSString *lastModifiedDate = [persistentNonResidentData objectForKey:#"lastModified"];
// Add your own code to compare the strings.
}
Or I may have misunderstood your question and that may not be what you are looking for at all lol.
Related
Basically, I'm writing a video file to the Application Support directory and then saving it's path:
NSString *guid = [[NSUUID new] UUIDString];
NSString *outputFile = [NSString stringWithFormat:#"video_%#.mp4", guid];
NSString *outputDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *tempPath = [outputDirectory stringByAppendingPathComponent:outputFile];
NSURL *fileURL = [NSURL fileURLWithPath:tempPath]
// code to write a video to fileURL
I save the string path itself by calling [fileURL path];
Now later when I try to create an AVAssetItem from it, I can't actually get it to play.
EDIT:
Ok, so it seems the issue is the space in Application Support. I tried saving the file to just the Library directory and it worked fine.
So the question becomes, how can I play a video I save in the Application Support directory. I wasn't able to create a valid NSURL without escaping the space (when I tried it would return a nil NSURL) but it seems that the space/escaping doesn't allow it to play correctly.
Assume the NSURL to NSString conversion is required (unless it really should be avoided).
Also, side note, but if anyone could give some insight as to why this question was down voted so I can improve the quality of my questions, that would be appreciated. I don't understand?
While I am not informed enough to opine about whether this would change if I had used matt's recommended methods: URLForDirectory:inDomain:appropriateForURL:create:error: and URLByAppendingPathComponent: the actual issue here isn't converting an NSURL to NSString
It's that I'm saving the full URL ([fileURL path]). This is because the [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0]; dynamically changes and the full path won't always point me to the appropriate file.
Instead I need to save just the name of my file (in my case I needed to persist outputFile) and then later dynamically build the the full path when I need it.
The same exact process:
NSString *outputDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *tempPath = [outputDirectory stringByAppendingPathComponent:outputFile];
NSURL *fileURL = [NSURL fileURLWithPath:tempPath];
worked just fine.
TLDR: Saving a full path doesn't work
When I try to log all available editors on my system for my temporary file (which is "toString" in this code) it always returns null, although I have many applications installed on my system.
NSArray *appUrls = (NSArray*)LSCopyApplicationURLsForURL((CFURLRef)[NSURL URLWithString:toString], kLSRolesViewer | kLSRolesEditor);
toString is containing the following file path:
/var/folders/pl/tcc5k3fd6tj2__9dprg9dm1m0000gp/T/tempFile
What should be the problem here?
[NSURL URLWithString:toString]
expects a complete URL string including scheme, such as "file://var/folders/...".
Use
[NSURL fileURLWithPath:toString]
instead to get a file URL with the specified path.
Another problem could be that your file name does not have any file extension (e.g. ".txt"), because Launch Services uses the extension (or file type/creator) to find a suitable application.
I was struggling with the this and I wanted to get all Bundles that could open a determined path/file extension.
If you have a file extension, you can get all bundles that can edit it by the following:
//All Bundle Ids
NSString *pathExtension = #"docx";
CFArrayRef utisRef = UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef) pathExtension,nil);
NSLog( #"UTI: utisRef %#", utisRef);
NSArray *utis = CFBridgingRelease(utisRef);
NSMutableSet *mutableSet = [[NSMutableSet alloc] init];
for (NSString *uti in utis) {
CFArrayRef bundleIDsRef = LSCopyAllRoleHandlersForContentType((__bridge CFStringRef) uti,kLSRolesEditor);
[mutableSet addObjectsFromArray:CFBridgingRelease(bundleIDsRef)];
}
NSLog( #"bundleIDs: %#", mutableSet);
If you have a path of file and you want to get all apps location that can edit it, you can use the following:
//Location of apps
NSString *str = #"/Users/ricardoanjos/Library/Developer/Xcode/DerivedData/EgnyteDrive-hforbniifiojczefbnwanzxakvlr/Build/Products/Debug/1.pdf";
NSURL* url = [[NSURL alloc] initFileURLWithPath:str];
CFURLRef urlRef = (__bridge CFURLRef)url;
CFArrayRef appUrlsRef = LSCopyApplicationURLsForURL(urlRef, kLSRolesEditor);
NSArray *appUrls = CFBridgingRelease(appUrlsRef);
NSLog(#"appUrls: %#", appUrls);
I hope it will help.
I am developing a cocoa application. I created a user default object with this code:
NSUserDefaults *standard_user_defaults = [NSUserDefaults standardUserDefaults];
if (standard_user_defaults) {
[standard_user_defaults setObject:myString forKey:key];
[standard_user_defaults synchronize];
}
And then, I am getting the value with this code:
NSUserDefaults *standard_user_defaults = [NSUserDefaults standardUserDefaults];
NSString *val = nil;
if (standard_user_defaults)
val = [standard_user_defaults objectForKey:key];
The problem is that if I run this application in my computer is working fine, and I can get the user default value, but if another person runs the application is getting an empty value. Any ideas?
I assume by “another person” you mean another user on your computer. The reason they cannot read what you’ve written to your user defaults is because NSUserDefaults is meant for user-specific values. To save something that your application will be able to access from both your user account and others’, you need to place it in a file stored somewhere in a shared location on the system. One way to do this would be something like this:
NSString *basePath = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSLocalDomainMask, YES) objectAtIndex:0];
NSString *filePath = [basePath stringByAppendingPathComponent:#"someFileName"];
// write:
NSDictionary *dataToSave = [NSDictionary dictionaryWithObjectsAndKeys:theValue, #"someKey", nil];
if(filePath && [dataToSave writeToFile:filePath atomically:YES])
{
// success!
}
// read:
NSDictionary *dataToLoad = [NSDictionary dictionaryWithContentsOfFile:filePath];
if(dataToLoad)
{
// success!
NSString *theValue = [dataToLoad objectForKey:#"someKey"];
}
You should try and use valueForKey rather than objectForKey. Looking at your code, there is no reason it shouldn't work for one person and not another.
The one exception would be if you are using the defaults from the settings.bundle. Values stored into settings.bundle for user defaults are not initialized until the user opens the settings page for the first time.
I'm looking to record audio from the iPhone microphone via a record button. I have downloaded and got to grips with the sample project Apple provides to do this (SpeakHere).
However as a next step, I'd like to save the users recording in a "playlist" style (not using the iTunes playlist, rather a local playlist).
Is it possible to do this using Objective-C (as opposed to the C implementation currently provided) - ideally CoreData would be used to store the audio.
Thanks
Here's how I did it:
1) Find the temp file that the SpeakHere code creates -- look for the .caf extension in the SpeakHereController class. Then move that temp file to your application directory with something like this:
NSString *myFileName = #"MyName"; // this would probably come from a user text field
NSString *tempName = #"recordedFile.caf";
NSString *saveName = [NSString stringWithFormat:#"Documents/%#.caf", myFileName];
NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempName];
NSString *savePath = [NSHomeDirectory() stringByAppendingPathComponent:saveName];
2) Save some metadata about the file, at least its name. I'm putting that into NSUserDefaults like this:
NSDictionary *recordingMetadata = [NSDictionary dictionaryWithObjectsAndKeys:
myFileName, #"name",
[NSDate date], #"date",
nil];
[self.savedRecordings addObject:recordingMetadata]; // savedRecordings is an array I created earlier by loading the NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:self.savedRecordings forKey:#"recordings"]; // now I'm updating the NSUserDefaults
3) Now you can display a list of saved recordings by iterating through self.savedRecordings.
4) When the user selects a recording, you can easily initialize an AVAudioPlayer with the selected file name and play it back.
5) To let users delete recordings, you can do something like this:
NSString *myFileName = #"MyName";
// delete the audio file from the application directory
NSString *fileName = [NSString stringWithFormat:#"Documents/%#.caf", myFileName];
NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL];
// delete the metadata from the user preferences
for (int i=0; i<[self.savedRecordings count]; i++) {
NSDictionary *thisRecording = [self.savedRecordings objectAtIndex:i];
if ([myFileName isEqualToString:[thisRecording objectForKey:#"name"]]) {
[self.savedRecordings removeObjectAtIndex:i];
break;
}
}
[[NSUserDefaults standardUserDefaults] setObject:self.savedRecordings forKey:#"recordings"];
Note that if you save the audio files into the Documents folder and enable "Application supports iTunes file sharing" in your info.plist, then users can copy their recordings out of the app and save them onto their computers ... a nice feature if you want to offer it.
I'm using a text file to save the changes made by a user on a list (the reason that I'm doing this is so that I can upload the text file to a PC later on, and from there insert it into an Excel spreadsheet). I have 3 data structures: A NSMutableArray of keys, and a NSMutableDictionary who's key values are MSMutableArrays of NSStrings.
I iterate through these data structures and compile a file string that looks much like this:
(Key);(value)\t(value)\t(value):\n(Key);(value).. .so on.
SO, onto the actual question: When I attempt to save it, it fails. I'm 99% sure this is because of the file path that I'm using, but I wanted backup to check this out. Code follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [paths objectAtIndex:0];
NSString *fileString = [NSString stringWithString:[self toFileString]];
if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL]){
NSLog(#"File save failed");
} else {
// do stuff
}
(Code above is re-copied, since the actual code is on a different computer. It compiles, so ignore spelling errors?)
I tried using NSError, but I got bogged down in documentation and figured I might as well ask SO while trying to figure out how to properly use NSError (might be a little bit of an idiot, sorry).
99% sure it's the NSArray *paths line that's tripping it up, but I don't know how else to get the documents directory.
Edit: Problem solved, and one final question: If I save it to the App's document directory, where can I go after I close the app to see if it saved properly? If it works like I think it does, isn't it sandboxed in with the app's installation on the simulator? (i.e. no way of checking it)
NSLog() that filePath string. I think you're trying to write to the directory itself, not to a file.
Try this instead:
filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"myfile.txt"];
What is the file name you want to save? The method
NSArray *paths = NSSearchPathForDirectoriesInDomains(...);
NSString *filePath = [paths objectAtIndex:0];
...
if(![fileString writeToFile:filePath ...
means you are saving the string into a file path which has the same name as a folder. This will of course fail. Please give it a name, e.g.
NSString* fileName = [filePath stringByAppendingPathComponent:#"file.txt"];
if(![fileString writeToFile:fileName ...
and try again.
BTW, to use NSError:
NSError* theError = nil;
if(![fileString writeToFile:fileName ... error:&theError]) {
// ^^^^^^^^^
NSLog(#"Failed with reason %#", theError);
// theError is autoreleased.
}