Stop copying file in Cocoa - objective-c

I'm facing a problem I using the FSEvent to catch the file created in my app but I also want to check the file extension (ex: photo/ video type) are allowed, otherwise these files will not be copied. How can I check the file extension before copying in Cocoa? Any suggestion would be appreciated. Thanks
Please note I don't use NSFileManager to copy file.

Use following code to check for movies or audio file types. Full list of UTI-Types in the documentation.
NSString * fileUTI = nil;
BOOL success = [self getResourceValue:& fileUTI forKey:NSURLTypeIdentifierKey];
if (success && [uti isKindOfClass:[NSString class]]) {
BOOL fileConformsToUTI = UTTypeConformsTo(fileUTI, kUTTypeMovie);
fileConformsToUTI = fileConformsToUTI || UTTypeConformsTo(fileUTI, kUTTypeAudio);
// check and do the copy
}

Related

While loop with NSFileManager directory enumerator not running

I am seeking help to understand why a tutorial I am following is not working for me. I am running macOS 12.3.1, Xcode 13.3.1. The project is in Objective-C and using XIB.
This is a view-based NSTableView, using a folder of PNGs stored on my SSD for the imageView and the stringByDeletingPathExtension as stringValue for the cell's text field. I filled my code with NSLog calls to try and catch what could have been going awry.
Most setup is happening in applicationDidFinishLaunching:, where I initialise an NSMutableArray for the table's content, an NSString for the file path, then set up the file manager and the directory enumerator with said path (note: all working up to here).
Now comes the loop to populate the table contents' mutable array. I cannot understand why said loop gets skipped entirely! Its condition is to set an NSString equal to the nextObject of the directory enumerator. I am sure the loop gets skipped because the NSLog call after the loop runs!
Here is the entire code of applicationDidFinishLaunching:, including my comments and logs (I have just replaced my account name with ):
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_tableContents = [[NSMutableArray alloc] init];
NSString *path = #"/Users/<myUsername>/Developer/Apple-Programming-YT/Cocoa Programming/Flags/PNG/40x30";
// MARK: Debug 1
NSLog(#"path found: %#", path); // the correct path gets printed, as expected
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDirectoryEnumerator *directoryEnum = [fileManager enumeratorAtPath:path];
NSString *file;
// MARK: Debug 2
NSLog(#"Checking that file is empty: %#", file); // (null) gets printed, as expected
// MARK: Debug 3
if (file != directoryEnum.nextObject) {
NSLog(#"File cannot be assigned to the Directory Enumerator");
} else if (file == directoryEnum.nextObject) {
NSLog(#"File properly assigned. Proceed!"); // this gets printed! Is it correct?
} else {
NSLog(#"Something went wrong during assignment of nextObject to file");
}
while (file = [directoryEnum nextObject]) {
NSLog(#"While loop entered!"); // this doesn't get printed! Why?!
// MARK: Debug 4
NSLog(#"File: %#", file);
NSString *filePath = [path stringByAppendingFormat:#"/%#", file];
// MARK: Debug 5
NSLog(#"Image filepath: %#", filePath);
NSDictionary *obj = #{#"image": [[NSImage alloc] initByReferencingFile:filePath],
#"name": [file stringByDeletingPathExtension]};
[self.tableContents addObject:obj];
}
[self.tableView reloadData];
NSLog(#"Table View Reloaded"); // This gets printed!
}
I have uploaded the full app to GitHub, in case you may want to look at it and see if something else could be wrong, but every outlet, delegate, data source is connected.
Now for my diagnosis & ideas:
The Debug 3 mark is what I find most interesting. AFAIK file should still be (null), so how checking if it is equal to directoryEnum.nextObject returns YES?
I created Debug 3 because the NSLog checking whether the loop had been entered didn't get printed. I therefore assumed the condition for the while loop had a problem.
I then tried to create a do-while loop instead of this while loop and, of course, the code ran. For the log with "Image filepath" it returned the address above followed by (null), as if it didn't find the file. But how is it possible if the file is indeed there? Do I require some sort of permission to access it? Being the object empty, the next line in the console was quite clear: "attempt to insert nil object from objects[1]".
But now, how do I solve this?
Any help here is much appreciated. If you download it from GitHub, please replaces the *path string with a folder of PNGs on your SSD.
Thank you.
I don't think you can access the filesystem directly with a path like that any more. If you check the value of file in your code, it is nil, which means that file == directoryEnum.nextObject will evaluate to true.
You have to create a path starting with NSHomeDirectory() or similar and add components to it. This makes a path that goes via your application support folder, which contains an alias to the desktop. I'm not sure why that's OK and accessing it directly is not, but I'm not a Mac developer.
I'd have to say following a tutorial as old as that, you're going to struggle with a lot of things.

Swift: cannot preload Coredata

When I include a SQLite file with Objective-C under "Target - Build Phases - Copy Bundle Resources", this file will be completely copied to the target, i.e. device or simulator. On the target, I get the whole file: tables and contents (Records/rows).
Doing the same with Swift, the tables will be copied, but they are empty: no records/rows. :-(
Can I do something additional? Is there any "trick"?
How can I preload my core data with base-records using Swift???
I'm using Xcode v6.1.1, with Beta 6.2 it is the same.
This is my solution (for sqlite-files). I don't know why it is so "extravagant" in Swift. Perhaps there's an easier way?
Many THX to pbasdf!!!
Important: beneath the *.sqlite-file you must add the *.sqlite-shm and the *.sqlite-wal to your project
This files will automatically be added to "Target - Build Phases - Copy Bundle Resources"
This is based on the Standard-Template for the "AppDelegate.swift"…
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let dataName = „MyData.sqlite" // must be replaced by the name of your file!
let modelName = „MyData“ // must be replaced by the name of your model!
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(dataName)
let fileManager = NSFileManager.defaultManager()
let bundle = NSBundle.mainBundle()
let fromURL = bundle.URLForResource(modelName, withExtension: "sqlite")
// check sqlite-file: must it be installed?
if !fileManager.fileExistsAtPath(url.path!) {
self.copySqlliteFiles(modelName, databaseName: dataName)
}
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
...}
return coordinator
}()
// MARK: - copy sqllite-files (c) by Ray Wenderlich & Team in „“Core Data by Tutorials“
// check sqlite-files: they must be installed...
func copySqlliteFiles(modelName: String, databaseName: String)
{
let bundle = NSBundle.mainBundle()
let baseDatabaseURL = bundle.URLForResource(modelName, withExtension: "sqlite")
let documentsURL = self.applicationDocumentsDirectory
let storeURL = documentsURL.URLByAppendingPathComponent(databaseName)
NSFileManager.defaultManager().copyItemAtURL(baseDatabaseURL!, toURL: storeURL,error: nil)
let baseSHMURL = bundle.URLForResource(modelName,withExtension: "sqlite-shm")
let shmURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent(databaseName + "-shm")
NSFileManager.defaultManager().copyItemAtURL(baseSHMURL!, toURL: shmURL, error: nil)
let walURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent(databaseName + "-wal")
let baseWALURL = bundle.URLForResource(modelName, withExtension: "sqlite-wal")
NSFileManager.defaultManager().copyItemAtURL(baseWALURL!, toURL: walURL, error: nil)
}
I think this probably has to do with how files are laid out on iOS. Specifically:
when you copy the source file (by Xcode or at install time) it gets placed into the application bundle/container directory (which is read-only)
The standard CoreData initialization code looks for the data store in the Documents directory.
Not finding your source data (because it's in the wrong place), CoreData creates a new empty store.
To fix this, you need to copy the store from the Application directory to the Documents directory if it doesn't already exist.
This link gives an explanation and example code to do that. But in Objective-C it looks like:
NSString *storePath = [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"MyDB.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
// Copy the default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:#"MyDB" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath
toPath:storePath error:NULL];
}
}
I had a related issue related to this. I started working on some tutorials which used this line:
documentsURL.URLByAppendingPathComponent("StoreName")
At some point this stopped working. Data was not being written to the store anymore. To see why data was not being written, I tried opening the sqlite file but could not find it on my mac at the URL being used. The app folder was there, but there was no sqlite file. I looked back at older core data projects and found they used .sqlite extension. So I modified the line to:
documentsURL.URLByAppendingPathComponent("StoreName.sqlite")
This seemed to work so give it a try.

fileExistsAtPath returns NO for a directory that exists

fileExistsAtPath is returning NO for a directory that exists. If I have the following code:
NSURL *newDirectoryPath = ... // path does not begin with a tilda
// and is created using URLByAppendingPathComponent:isDirectory:
BOOL directoryExists = [self.fileManager fileExistsAtPath:[newDirectoryPath absoluteString]];
if (NO == directoryExists)
{
BOOL ret = [self.fileManager moveItemAtURL:self.currentPresentationDirectoryPath toURL:newDirectoryPath error: &err];
// ret is YES, err is nil
directoryExists = [self.fileManager fileExistsAtPath:[newDirectoryPath absoluteString]];
}
Even though the directory has just been created successfully with moveItemAtURL, fileExistsAtPath is still returning NO.
I know the documentation says this:
Attempting to predicate behavior based on the current state of the
file system or a particular file on the file system is not
recommended. Doing so can cause odd behavior in the case of file
system race conditions.
But I want to understand what the issue is here - if I close the app and relaunch it then the first check for fileExistsAtPath in the code above is still returning NO, even though the directory was previously successfully created during the prior execution of the code, and I can see the directory in the Organizer, and I can also successfully read from the contents of the directory etc. etc.
P.S. is there no fileExistsAtURL: method?
If you have an NSURL, -absoluteURL won't return a usable path for NSFileManager. It will return the absolute URL with the file:// prefix. E.g.: file:///path/to/file.
Instead try to use an other method, like -path. Check if that works.
NSURL *myURL = /* some url */;
NSString *myPath;
BOOL exi;
myPath = [myURL path];
exi = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if(!exi) {
NSLog(#"File does not exist");
}

How to make a directory iOS?

Okay,
So I have a Cydia app that I need to update. I am aware with Cydia apps that they don't have a Documents folder, so you have to make one. And here's how I made it before in iOS 4 (which doesn't work on iOS 5):
mkdir("/var/mobile/Library/APPNAME", 0755);
mkdir("/var/mobile/Library/APPNAME/Documents", 0755);
NSString *foofile = #"/var/mobile/Library/APPNAME/Documents/database.db";
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
if (fileExists == TRUE) {
NSLog(#"already exists");
} else {
NSLog(#"doesn't exists");
NSFileManager *fileManager = [[NSFileManager defaultManager]autorelease];
NSError *error;
NSString *documentDBFolderPath = #"/var/mobile/Library/APPNAME/Documents/database.db";
NSString *resourceDBFolderPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.db"];
[fileManager copyItemAtPath:resourceDBFolderPath toPath:documentDBFolderPath error:&error];
}
I also included code that copies the database file to that folder, too. That doesn't work (even when I create the folder manually via SSH).
Please help! Thanks.
Here is the method I made to create directories
-(void)createDirectory:(NSString *)directoryName atFilePath:(NSString *)filePath
{
NSString *filePathAndDirectory = [filePath stringByAppendingPathComponent:directoryName];
NSError *error;
if (![[NSFileManager defaultManager] createDirectoryAtPath:filePathAndDirectory
withIntermediateDirectories:NO
attributes:nil
error:&error])
{
NSLog(#"Create directory error: %#", error);
}
}
Try using createDirectoryAtURL:withIntermediateDirectories:attributes:error:.
NSFileManager Class Reference:
createDirectoryAtURL:withIntermediateDirectories:attributes:error:
Creates a directory with given attributes at the specified path.
Parameters
url - A file URL that specifies the directory to create.
If you want to specify a relative path, you must set the
current working directory before creating the corresponding
NSURL object. This parameter must not be nil.
createIntermediates - If YES, this method creates any non-existent
parent directories as part of creating the directory in url. If NO,
this method fails if any of the intermediate parent directories does
not exist. This method also fails if any of the intermediate path
elements corresponds to a file and not a directory.
attributes - The file attributes for the new directory and any newly created
intermediate directories. You can set the owner and group numbers,
file permissions, and modification date. If you specify nil for this
parameter or omit a particular value, one or more default values are
used as described in the discussion. For a list of keys you can
include in this dictionary, see “Constants” (page 54) section lists
the global constants used as keys in the attributes dictionary. Some
of the keys, such as NSFileHFSCreatorCode and NSFileHFSTypeCode, do
not apply to directories.
error - On input, a pointer to an error object. If an error occurs,
this pointer is set to an actual error object containing the error
information. You may specify nil for this parameter if you do not
want the error information.
Return Value
YES if the
directory was created or already exists or NO if an error occurred.
Check NSFileManager's class reference. To create folders you need createDirectoryAtPath:withIntermediateDirectories:attributes:error:
Superb Techotopia explanation of iOS5 filesystem
In Swift, returns true if exists or created.
func ensureDirectoryExists(path:String) -> Bool {
if !NSFileManager.defaultManager().fileExistsAtPath(path) {
do {
try NSFileManager.defaultManager().createDirectoryAtPath(path, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error)
return false
}
}
return true
}

File Handling in Objective C

Is there anyway to do Files Handling in Objective-C? I am just trying to do simple read and write and can use 'c' but i am force to use Objective-C classes for that :#. I am looking into NSInputStream, but its going over my head. Is there any tutorial which explains how to use NSInputStream?
I had trouble with basic file i/o when I first hit it in Obj-C as well. I ended up using NSFileHandle to get C style access to my file. Here's a basic example:
// note: myFilename is an NSString containing the full path to the file
// create the file
NSFileManager *fManager = [[NSFileManager alloc] init];
if ([fManager createFileAtPath:myFilename contents:nil attributes:nil] != YES) {
NSLog(#"Failed to create file: %#", myFilename);
}
[fManager release]; fManager = nil;
// open the file for updating
NSFileHandle *myFile = [NSFileHandle fileHandleForUpdatingAtPath:myFilename];
if (myFile == nil) {
NSLog(#"Failed to open file for updating: %#", myFilename);
}
// truncate the file so it is guaranteed to be empty
[myFile truncateFileAtOffset:0];
// note: rawData is an NSData object
// write data to a file
[myFile writeData:rawData];
// close the file handle
[myFile closeFile]; myFile = nil;
If all you need to do is really simple I/O, you can just tell an object to initialize itself from, or write itself to, a filesystem path or URL. This works with several Foundation classes, including NSString, NSData, NSArray, and NSDictionary among others.
Try starting out by looking at the following two NSString methods:
- initWithContentsOfFile:encoding:error:
- writeToFile:atomically:encoding:error:
I find apple's guides short and to the point.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html