Weird behavior of fopen on ios - objective-c

I am trying to create a file by fopen and then write it, but weird things happened.
When I plug in the iphone to the usb port. Everything works fine. A file is created at the tmp directory or the document directory as expected.
When I plug off the device and do the same thing, the file did not appear. I was wondering why.
I use fopen to create the file. In my case, I should do this to create and then write the file. The call is fopen(pcm_output, "wb+");

You need to use this call.
char const *path = [fileManager fileSystemRepresentationWithPath:url.path];
From the docs...
fileSystemRepresentationWithPath:
- (const char *)fileSystemRepresentationWithPath:(NSString *)path
iOS (2.0 and later)
Returns a C-string representation of a given path that properly encodes Unicode strings for use by the file system.
path: A string object containing a path to a file.
A C-string representation of path that properly encodes Unicode strings for use by the file system.

You're probably writing outside of the sandbox, can you post the path?
Just as a test try to turn on iTunes Sharing (this should have no effect, it's just a test) for your app.
EDIT:
After testing I discovered that you have to use:
NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [docsPath stringByAppendingPathComponent:[NSString stringWithCString:pcm_output encoding:NSUTF8StringEncoding]];
fopen([filePath UTF8String], "wb+");
Instead of just:
fopen([filePath UTF8String], "wb+");

NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask, YES
);
NSString* docDir = [paths objectAtIndex:0];
_tempLogPath = [docDir stringByAppendingPathComponent: #"Aisound5_CBLog.log"];
_tempPcmPath = [docDir stringByAppendingPathComponent: #"OutPcm.pcm"];
_tempWavPath = [docDir stringByAppendingPathComponent: #"OutWav.wav"];
tts_resource = [[bundle pathForResource:#"Resource_dev" ofType:#"irf"] UTF8String];
tts_log = [_tempLogPath UTF8String];
pcm_output = [_tempPcmPath UTF8String];
wav_output = [_tempWavPath UTF8String];
The original code is this, in which tts_resource tts_log pcm_output and wav_output are defined in a .h file and used in a .c file with fopen.
I had tried in your way to init the const string with the explicit const char* style, but the problem remains the same.

Related

Reading text file as a floating number in Objective-C

I have a text file with a single floating number in it that needs to be opened and read as a floating value so I can use it in my equations. I have successfully read in the text file as a string and checked it in UITextView to make sure the file was in the correct path etc. Below is how I am reading the file and I have tried using an NSScanner to read the number as a float but cannot get it to work.
NSString *filePath2 = [[NSBundle mainBundle] pathForResource: #"time" ofType: #"txt"];
NSString *textFromFile = [NSString stringWithContentsOfFile:filePath];
// i know the above won't work because it reads the file as a string
Use
[textFromFile doubleValue];

Is it possible to glob a directory in iOS and have the results cached by the OS?

Many methods of reading from a filesystem using NSFileManager and lower level APIs on iOS involve built-in caching, so reading from directories that haven't changed can be quite fast, even if there's lots of items in the directories.
I have a situation where I want to be able to enumerate files in a directory using a glob:
i.e. the folder has files named like this:
1-1-0.png
1-2-0.png
1-3-0.png
1-3-1.png
2-2-1.png
5-1-1.png
5-1-2.png
5-2-1.png
5-3-0.png
6-1-1.png
...
1501-5-2.png
I might want to get all filenames matching 5-*-1.png, which would give me back 5-1-1.png and 5-2-1.png.
Loading the whole directory listing then doing the globbing in RAM is pretty straightforward, but is there a method for doing this at an OS level that would have caching built-in, so repeated calls to the same glob would give cached (faster) results?
You can use the glob() function as outlined in this gist: https://gist.github.com/bkyle/293959
#include <glob.h>
+ (NSArray*) arrayWithFilesMatchingPattern: (NSString*) pattern inDirectory: (NSString*) directory {
NSMutableArray* files = [NSMutableArray array];
glob_t gt;
NSString* globPathComponent = [NSString stringWithFormat: #"/%#", pattern];
NSString* expandedDirectory = [directory stringByExpandingTildeInPath];
const char* fullPattern = [[expandedDirectory stringByAppendingPathComponent: globPathComponent] UTF8String];
if (glob(fullPattern, 0, NULL, &gt) == 0) {
int i;
for (i=0; i<gt.gl_matchc; i++) {
size_t len = strlen(gt.gl_pathv[i]);
NSString* filename = [[NSFileManager defaultManager] stringWithFileSystemRepresentation: gt.gl_pathv[i] length: len];
[files addObject: filename];
}
}
globfree(&gt);
return [NSArray arrayWithArray: files];
}

sqlite3_prepare_v2 is getting SQLITE_ERROR

I have been at this for hours and MUST get this working! It is holding up an iPhone app release... My first time using SQLite. I have followed all the advice and yet my sqlite3_prepare_v2 call gets a SQLITE_ERROR (1) every time!
Here is my code from my controller:
NSString *query = #"SELECT * FROM QandA ORDER BY random() LIMIT 1";
// const char *sqlStatement = "SELECT * FROM QandA ORDER BY random() LIMIT 1";
sqlite3_stmt *compiledStatement;
// sqlite3_stmt *statement;
int prepareStatus = sqlite3_prepare_v2(database, [query UTF8String],
-1, &compiledStatement, NULL);
if (prepareStatus == SQLITE_OK) {...
You'll note that I've tried using a "char *" also to no avail (among other attempts). My database opens fine with:
databaseName = #"Facts.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"databasePath = %#", databasePath);
int dbOpenStatus = sqlite3_open_v2([databasePath UTF8String], &database, SQLITE_OPEN_READWRITE, NULL);
From my controller interface:
NSString *databaseName;
NSString *databasePath;
I've checked in the debugger and everything looks good, but the prepare statement fails. I don't know how to log the statement it is trying to compile... I assume/hope it is just what my SELECT says.
Can anyone help? I'm desperate. Mark
Found the answer here. I had to use this instead for the path to the DB file:
[[NSBundle mainBundle]pathForResource:#"Facts"extension:#"sqlite"];
This gave a slightly different path (one extra directory) - once I used that it worked! Hope this helps someone else... I spent many hours on this.
Please note that the above is slightly inaccurate, the ofType should be used not extension, and the ofType obviously needs to match your file extension.
If your DB is named "mysqldatabase.sql" change your path URL to:
databasePath = [[NSBundle mainBundle]pathForResource:#"mysqldatabase" ofType:#"sql"];
The code seems to be fine, might be a silly question but have you created the QandA table inside of the database?

CFURLCreateDataAndPropertiesFromResource failed with error code -15

I'm trying to lead a TIFF image into a CGImageSource using CGImageSourceCreateWithURL. My URL is correct, is %20 encoded, is a path to an existing file, that file is a TIFF. However, the line:
NSString *fullPath = [[[fileNames objectAtIndex:pageNum - 1] string]
stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];
NSString* urlStr = [NSString stringWithFormat: #"file:/%#", fullPath];
NSURL * url = [NSURL URLWithString: fullPath];
CGImageSourceRef src = CGImageSourceCreateWithURL (url, NULL);
gives me the following error:
Wed Aug 4 20:17:20 Brians-mini.local DocKeep[17199] <Error>: CGImageSourceCreateWithURL: CFURLCreateDataAndPropertiesFromResource failed with error code -15.
anyone know what error -15 is? or where I can find it?
thanks
ETA: I should also mention that I don't have this problem if the path doesn't have spaces in it... (Spaces in filenames are an abomination unto the lord, but I suppose we have to live with them... B-)
Don't use +URLWithString: for this. Instead use the purpose-built +fileURLWithPath:
Try this:
NSString *fullPath = [[fileNames objectAtIndex:pageNum - 1] string];
CFStringRef fullPathEscaped = CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)pdfPath, NULL, NULL,kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithString(NULL, fullPathEscaped, NULL);
CGImageSourceRef src = CGImageSourceCreateWithURL (url, NULL);
Error code -15 is is kCFURLImproperArgumentsError.
More info at Apple documentation for Core Foundation URL Access Utilities Reference.

How do I create a temporary file with Cocoa?

Years ago when I was working with C# I could easily create a temporary file and get its name with this function:
Path.GetTempFileName();
This function would create a file with a unique name in the temporary directory and return the full path to that file.
In the Cocoa API's, the closest thing I can find is:
NSTemporaryDirectory
Am I missing something obvious or is there no built in way to do this?
A safe way is to use mkstemp(3).
[Note: This applies to the iPhone SDK, not the Mac OS SDK]
From what I can tell, these functions aren't present in the SDK (the unistd.h file is drastically pared down when compared to the standard Mac OS X 10.5 file). I would use something along the lines of:
[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: #"%.0f.%#", [NSDate timeIntervalSinceReferenceDate] * 1000.0, #"txt"]];
Not the prettiest, but functional
Apple has provided an excellent way for accessing temp directory and creating unique names for the temp files.
- (NSString *)pathForTemporaryFileWithPrefix:(NSString *)prefix
{
NSString * result;
CFUUIDRef uuid;
CFStringRef uuidStr;
uuid = CFUUIDCreate(NULL);
assert(uuid != NULL);
uuidStr = CFUUIDCreateString(NULL, uuid);
assert(uuidStr != NULL);
result = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"%#-%#", prefix, uuidStr]];
assert(result != nil);
CFRelease(uuidStr);
CFRelease(uuid);
return result;
}
LINK :::: http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009245
see file :::AppDelegate.m
I created a pure Cocoa solution by way of a category on NSFileManager that uses a combination of NSTemporary() and a globally unique ID.
Here the header file:
#interface NSFileManager (TemporaryDirectory)
-(NSString *) createTemporaryDirectory;
#end
And the implementation file:
#implementation NSFileManager (TemporaryDirectory)
-(NSString *) createTemporaryDirectory {
// Create a unique directory in the system temporary directory
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:guid];
if (![self createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:nil]) {
return nil;
}
return path;
}
#end
This creates a temporary directory but could be easily adapted to use createFileAtPath:contents:attributes: instead of createDirectoryAtPath: to create a file instead.
If targeting iOS 6.0 or Mac OS X 10.8 or higher:
NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
Swift 5 and Swift 4.2
import Foundation
func pathForTemporaryFile(with prefix: String) -> URL {
let uuid = UUID().uuidString
let pathComponent = "\(prefix)-\(uuid)"
var tempPath = URL(fileURLWithPath: NSTemporaryDirectory())
tempPath.appendPathComponent(pathComponent)
return tempPath
}
let url = pathForTemporaryFile(with: "blah")
print(url)
// file:///var/folders/42/fg3l5j123z6668cgt81dhks80000gn/T/johndoe.KillerApp/blah-E1DCE512-AC4B-4EAB-8838-547C0502E264
Or alternatively Ssswift's oneliner:
let prefix = "blah"
let url2 = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(prefix)-\(UUID())")
print(url2)
You could use mktemp to get a temp filename.
The modern way to do this is FileManager's url(for:in:appropriateFor:create:).
With this method, you can specify a SearchPathDirectory to say exactly what kind of temporary directory you want. For example, a .cachesDirectory will persist between runs (as possible) and be saved in the user's library, while a .itemReplacementDirectory will be on the same volume as the target file.
Don't use legacy APIs like NSTemporaryDirectory, get a proper URL instead from FileManager.
let tmpURL = FileManager
.default
.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
You'd still have to create the directory.
You could use an NSTask to uuidgen to get a unique file name, then append that to a string from NSTemporaryDirectory(). This won't work on Cocoa Touch. It is a bit long-winded though.
Adding to #Philipp:
- (NSString *)createTemporaryFile:(NSData *)contents {
// Create a unique file in the system temporary directory
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:guid];
if(![self createFileAtPath:path contents:contents attributes:nil]) {
return nil;
}
return path;
}