File Handling in Objective C - 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

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.

Creating a USDZ file on the fly?

Anyone aware of how to create usdz from obj on the fly? Our application creates an obj file using a third party library and for using the QLPreviewController we need to convert it to usdz format. There are ways to do that using the terminal but wondering if there is any way to do it programmatically?
An engineer on my team figured this out last week!
Creating USDZ files is funny right now - currently we can fake it by saving a USDC file and... renaming the extension!
First you'll want to load the .obj file at filePath as an MDLAsset
NSURL *url = [NSURL fileURLWithPath:filePath];
MDLAsset *asset = [[MDLAsset alloc]initWithURL:url];
ensure the MDLAsset can write the desired extensions
usdc is supported (USD binary format)
if([MDLAsset canExportFileExtension:#"usdc"]){
NSLog(#"able to export as usdc");
// save the usdc file
[asset exportAssetToURL:usdcUrl];
}
rename the usdc to usdz because that's all it takes
NSError *renameErr;
NSFileManager *fm = [[NSFileManager alloc] init];
BOOL mvResult = [fm moveItemAtPath:usdcPath toPath:usdzPath error:& renameErr];
if(! mvResult){
NSLog(#"Error renaming usdz file: %#", [renameErr localizedDescription]);
}
Hope this helps until Apple can give us a more thorough how-to.
If you want to read a more long form breakdown of this - https://www.scandy.co/blog/how-to-export-simple-3d-objects-as-usdz-on-ios

OS X faster file system API than repetitively calling [NSFileManager attributesOfItemAtPath...]?

Is there a faster file system API that I can use if I only need to know if a file is a folder/symlink and its size. I'm currently using [NSFileManager attributesOfItemAtPath...] and only NSFileSize and NSFileType.
Are there any bulk filesystem enumeration APIs I should be using? I suspect this could be faster without having to jump in and out of user code.
My goal is to quickly recurse through directories to get a folders true file size and currently calling attributesOfItemAtPath is my 95% bottleneck.
Some of the code I'm currently using:
NSDictionary* properties = [fileManager attributesOfItemAtPath:filePath error:&error];
long long fileSize = [[properties objectForKey:NSFileSize] longLongValue];
NSObject* fileType = [[properties objectForKey:NSFileType isEqual:NSFileTypeDirectory];
If you want to get really hairy, the Mac OS kernel implements a unique getdirentriesattr() system call which will return a list of files and attributes from a specified directory. It's messy to set up and call, and it's not supported on all filesystems (!), but if you can get it to work for you, it can speed things up significantly.
There's also a closely related searchfs() system call which can be used to rapidly search for files. It's subject to most of the same gotchas.
You can use stat and lstat. Take a look at this answer for calculating directory size.
CPU raises with attributesOfItemAtPath:error:
Whether it's faster or not I'm not certain, but NSURL will give you this information via getResourceValue:forKey:error:
NSError * e;
NSNumber * isDirectory;
BOOL success = [URLToFile getResourceValue:&isDirectory
forKey:NSURLIsDirectoryKey
error:&e];
if( !success ){
// error
}
NSNumber * fileSize;
BOOL success = [URLToFile getResourceValue:&fileSize
forKey:NSURLFileSizeKey
error:&e];
You might also find it convenient to wrap this up if you don't really care about the error:
#implementation NSURL (WSSSimpleResourceValueRetrieval)
- (id)WSSResourceValueForKey: (NSString *)key
{
id value = nil;
BOOL success = [self getResourceValue:&value
forKey:key
error:nil];
if( !success ){
value = nil;
}
return value;
}
#end
This is given as the substitute for the deprecated File Manager function FSGetCatalogInfo(), which is used in a solution in an old Cocoa-dev thread that Dave DeLong gives the thumbs up to.
For the enumeration part, the File System Programming Guide has a section "Getting the Contents of a Directory in a Single Batch Operation", which discusses using contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error:

Zipping a folder in Objective C

Are there any libraries that work in Objective C for zipping entire folders (and decompressing them)? I have looked at some of them by searching but they look like they require adding files individually and some of them supposedly crash...
It looks like this library might work:
http://bitbucket.org/dchest/osxzip/overview
I don't know if it supports folders, however. Anyone know if it does or have any other libraries that support zipping folders? Even sample code for interacting with the command line libz would be fine with me...
You could use NSTask to run the command line ditto program. Be sure to look at the ditto man page for the right combination of flags to get Finder-compatible zipping.
According to this example: http://www.raywenderlich.com/1948/how-integrate-itunes-file-sharing-with-your-ios-app you can get a NSData Object with the Zipped Data and then just write it with [data writeToFile....]
- (NSData *)exportToNSData {
NSError *error;
NSURL *url = [NSURL fileURLWithPath:_docPath];
NSFileWrapper *dirWrapper = [[[NSFileWrapper alloc] initWithURL:url options:0 error:&error] autorelease];
if (dirWrapper == nil) {
NSLog(#"Error creating directory wrapper: %#", error.localizedDescription);
return nil;
}
NSData *dirData = [dirWrapper serializedRepresentation];
NSData *gzData = [dirData gzipDeflate];
return gzData;
}

How can text files be written using Objective-C?

In .NET, a file can be written to the file system using:
FileStream fs = File.Create(#"Filename");
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("Hello, World!");
// etc...
sw.Close();
fs.Close();
How would I achieve the same operation in Objective-C and Cocoa? I believe it involves the NSMutableData class, but I do not know how to implement it.
Tiny Mac Tutorials has a post on this.
The example code from that post is below:
// filetest.m
// Created by macateeny.blogspot.com Sept 2008.
// Copyleft (c) 2008. some rights reserved.
//
// Compile from the command line with:
// gcc filetest.m -Wall -o filetest -framework Foundation
//
#import <Foundation/Foundation.h>
// main entry point of our file test tool with the argument counter and vector
int main(int argc,char *argv[])
{
// allocate a memory pool for our NSString Objects
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// declare NSString Obj pointer and initialise it
NSString *str = #"Cooking with Objective-C\r\n";
// declare NSString filename and alloc string value
NSString *filenameStr = #"./filetest.txt";
// NSObject which contains all the error information
NSError *error;
// write contents and check went ok
if(![str writeToFile: filenameStr atomically: YES encoding:NSUTF8StringEncoding error:&error]) {
NSLog(#"We have a problem %#\r\n",[error localizedFailureReason]);
}
// unleash the allocated pool smithers
[pool release];
// The app is terminated
return 0;
}
Note that Objective C is a pure superset of standard C . Most of the usual posix library calls (in stdio, stdlib, etc.) are available and usable, as long as you don't try to use them to escape the app's sandbox (write to system directories, etc.)
So fopen() and fprintf() will also work perfectly well for writing ASCII or UTF8 text and data to files. You can use NSSearchPathForDirectoriesInDomains to find the appropriate directory names, and use various NSString convenience methods to convert NSStrings to UTF8.
See Apple's development docs - Cocoa concepts especially for this re Strings The Apple overview documents of the librairead all the concepts first it will give you a idea of what details you need
For the latest version of Cocoa on iOS or MacOS you can do this if you don't want to check for an error,
NSString *str = #"Wollah";
[str writeToFile:#"Wollah.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
NSString *str = #"Wollah";
[str writeToFile:#"/Wollah.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
Although using an NSURL is recommended, and I always do that.
NSString has a writeToFile method.