I'm trying to read files stored in assets folder and its subfolders using std::ifstream in an iOS app written mostly in C++ (The same code is also used in other, non-iOS projects), but they're not found. Example: there is a file assets/shaders/ortho2d.vert and I'm trying to load it like this:
std::ifstream vertFStream( vertFile ); // vertFile's contents is "assets/shaders/ortho2d.vert"
if (!vertFStream) {
std::cerr << vertFile << " missing!" << std::endl;
exit( 1 );
}
I've added the assets folder to the XCode project as a blue folder and it shows up in Targets->Copy Bundle Resources.
Try this:
NSBundle *b = [NSBundle mainBundle];
NSString *dir = [b resourcePath];
NSArray *parts = [NSArray arrayWithObjects:
dir, #"assets", #"shaders", #"ortho2d.vert", (void *)nil];
NSString *path = [NSString pathWithComponents:parts];
const char *cpath = [path fileSystemRepresentation];
std::string vertFile(cpath);
std::ifstream vertFStream(vertFile);
You may need to check the relative path from where the application is running and probably use a full path to ensure the file is found.
The fact that the open failed does not necessarily mean the file is not found, it just might not be readable at this moment. (Incorrect permissions or file locked).
exit(1) is rather drastic.
sorry but some punctuations:
on iOS using file system calls from C++ is highly discouraged for security issues and limited support from a security point of view.
calls to file system should be done afer you know decently iOS app
folder layout. (bundles, resources, Documents folder" and so on..)
otherwise it will fail. c) you can mix c++ and objC but definitively
is not a correct approach.
under iOS you must use swift or objC (excect in very limited cases)
use iOS APIs, exactly as under android you would use java
Related
I am wanting to write my own logs to a text file on my iPhone. I wrote up a quick method that writes a string to a file. Right now it saves it into the Documents directory, which, if on the device is going to be a pain to get off, since I can't just browse to it. Is there a better way to quickly get this file off the device after I have written to it?
/**
* Logs a string to file
*
* #version $Revision: 0.1
*/
+ (void)logWithString:(NSString *)string {
// Create the file
NSError *error;
// Directory
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"log.txt"];
// Get the file contents
NSData *localData = [NSData dataWithContentsOfFile:filePath];
if (localData) {
NSString *logString = [[NSString alloc] initWithData:localData encoding:NSUTF8StringEncoding];
string = [logString stringByAppendingFormat:#"%#\n", string];
[logString release];
}
// Write to the file
[string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
}//end
Add Application supports iTunes file sharing to your application target's build info in Xcode:
Then, you can easily browse, retrieve and delete any files created by the app from iTunes, right under Devices > Your device > Apps > File Sharing:
You may have to capture what number of logs you have created so far and create a new name for each log biased on that.
So you might save your last made logs name as a string in NSUserDefaults and get the number off the end of that and add one onto that captured int ready for the next name.
So if you have #"Log4" you can get the 4 out of that and make it 5 so that the next log is named "Log5"
Just my 2 cents :P
With regard to the 'How to get the file' part of the question
iExplorer, previously iPhone Explorer allows you to view your apps, including their documents folder without jailbreaking your devices.
In my experience (albeit of an older version), getting the files from the phone can be a little temporamental (i.e. I drag a file onto my desktop and although it creates the file, it doesn't write any of the data), you can get the files from your device.
I have a file called "0.ballpoint" that all I want to do is store some coordinates with (don't really want to use Core Data because it seem a little excessive). I placed it in my project but when I try doing this:
if ( access("0.ballpoint", F_OK) != -1) {
printf("file exists\n");
}
else {
printf("doesn't exist\n");
}
It says it "doesn't exist". Do I need to put the full path name? And if I do what do I do when I place it on the actual iPhone/iPod Touch?
CoreData is not meant for File access. There are other file API's available for iPhone.
Please go through NSFileManager and NSStream documentation.
For example, if your intention is to check if file exists at certain path,you may use
(add your file to resources)
NSString *filePath = [[NSBundle mainBundle] pathForResource: #"0" ofType: #"ballpoint"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
Prefer not to use low level stuff (C File handles) unless you have a valid reason to do so. Paths are to be carefully chosen (as you cant make assumptions on certain path, which might cease to exist in future).
Having said that, if you prefer to work with C file handling ,
NSString *filePath = [[NSBundle mainBundle] pathForResource: #"0" ofType: #"ballpoint"];
FILE *fileHandle = fopen([filePath cStringUsingEncoding:NSASCIIStringEncoding],"r");
it needs to become a resource to your Application that way the file can be pulled anytime during the use of your application.
Im not too well educated in Ipod Dev, im more of a windows phone 7 developer but i know it cant access it if it is still on your computer so you need to import it into your application some how and call it with the full file path name.
Does anyone know how I can extract the pixel data from a DICOM file and pass it to an image viewer on iOS?
Sorry if this is a simple question, but it seems to be a major component to a huge can of worms I have opened.
I'm using GDCM on iOS. I haven't pushed it very hard yet but it's working well so far. I basically followed the directions for hacking XCode projects to run in iOS in this excellent article on ITK.
Here's how I got it to compile for iOS:
Downloaded source from sourceforge, installed cmake via ports. You'll need a recent version of cmake (I'm using 2.8.2)
If the source is in a folder called gdcm-2.0.17/, then create another directory at that level (say gdcmbin), cd to that directory, and enter ccmake -GXCode ../gdcm-2.0.17/ in the terminal window. This creates the XCode project. When I did this I didn't create any of the example programs or create shared libraries (which won't work in iOS). Just run the defaults.
Follow the directions in the ITK paper on changing the build options (step #7 on page 4).
Then link GDCM into your project using the excellent instructions at Clint Harris' blog
When you're setting up the header search path in your project to GDCM - you have to enter two paths: blah/gdcm-2.0.17/Source/** and blah/gdcmbin/**. The trailing '/Source' on the first path is necessary - otherwise you get headers that aren't appropriate for your architecture.
One glitch (annoying but haven't spent the time to figure it out yet): you get a bunch of linking errors when you switch from simulator to device (or vice versa). This is because the gdcm project doesn't put the outputs into different directories for different targets. So - run a clean and rebuild in the gdcm project when you're switching. I'll probably get annoyed by this soon enough to change it :-).
Here's a rough snippet of how you'd call the library and put the results in an Objective-C dictionary:
NSMutableDictionary * imageDictionary = [[NSMutableDictionary alloc] initWithCapacity:40];
// The output of gdcm::Reader is a gdcm::File
gdcm::File &file = reader.GetFile();
// the dataset is the the set of element we are interested in:
gdcm::DataSet &ds = file.GetDataSet();
const Tag studyInstance(0x0020,0x000d); // Study Instance UID
const DataElement &dicomVal = ds.GetDataElement(studyInstance);
std::string stringVal( dicomVal.GetByteValue()->GetPointer(), dicomVal.GetByteValue()->GetLength() );
NSString *val = [NSString stringWithCString:stringVal.c_str() encoding:[NSString defaultCStringEncoding]];
[imageDictionary setObject:val forKey:#"studyInstanceUID"];
(Note: this is in an *.mm file that mixes C++ and ObjectiveC)
Imebra has an Objective-C wrapper that can be used also with Swift.
#import "imebraobjc/imebra.h"
// Get the DICOM dataset from file
NSError* error = nil;
ImebraDataSet* pDataSet = [ImebraCodecFactory loadFromFile:#"test.dcm" error:&error]
// CHeck the patient name (automatically convert from DICOM charsets to UTF-8)
NSString* checkPatientName = [pDataSet getString:[[ImebraTagId alloc] initWithGroup:0x10 tag:0x10] elementNumber:0 error:&error]; // Automatically converted to UTF-8 if necessary
// Get the frame 0
ImebraImage* pCheckImage = [pDataSet getImageApplyModalityTransform:0 error:&error];
// Get the image data
ImebraReadingDataHandlerNumeric* readingDataHandler = [pCheckImage getReadingDataHandler:&error];
// Scan the pixels. Access the data handler memory for faster data access
for(unsigned int pixel(0); pixel != readingDataHandler.size; ++pixel)
{
unsigned int pixelValue = [readingDataHandler getUnsignedLong:pixel error:&error];
}
ImebraDrawBitmap* pDrawBitmap = [[ImebraDrawBitmap alloc] init];
// Obtain a NSImage (UIImage on iOS)
NSImage* pNsImage = [pDrawBitmap getImebraImage:pCheckImage error:&pError];
If you want to find DICOM software, look at idoimaging.com, a clearinghouse for medical imaging software. You can choose your platform, input format, output format, language, etc. iOS isn't listed as a format, but much of the software listed there is available with source, useful in library form, and available for MacOS X. For example, I selected:
input format: DICOM
platform: Macintosh
language: C
and found several packages. Given the similarities between MacOS and iOS and the fact that some of these are cross-platform with source included, it shouldn't be too difficult to get one of them working on iOS.
I'm trying to create a folder inside the /sounds folder of my app.
-(void)productPurchased:(UAProduct*) product {
NSLog(#"[StoreFrontDelegate] Purchased: %# -- %#", product.productIdentifier, product.title);
NSFileManager *manager = [NSFileManager defaultManager];
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSError *error;
NSString *dataPath = [NSString stringWithFormat:#"%#/sounds/%#", bundleRoot, product.title];
if (![manager fileExistsAtPath:dataPath isDirectory:YES]) {
[manager createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:&error];
NSLog(#"Creating folder");
}
NSLog(#"%#", error);
}
But I get this error:
Error Domain=NSCocoaErrorDomain Code=513 "The operation couldn’t be completed. (Cocoa error 513.)" UserInfo=0x175120 {NSFilePath=/var/mobile/Applications/D83FDFF9-2600-4056-9047-05F82633A2E4/App.app/sounds/Test Tones, NSUnderlyingError=0x117520 "The operation couldn’t be completed. Operation not permitted"}
What am I doing wrong?
Thanks.
If you search Google on the error domain NSCocoaErrorDomain you find that the code 513 translates to the error NSFileWriteNoPermissionError.
This provides you with the critical clue for solving this problem:
This is the bundle directory containing the application itself. Because an application must be signed, you must not make changes to the contents of this directory at runtime. Doing so may prevent your application from launching later.
Specifically, you cannot modify the contents of a compiled app's bundle folder. This is because the bundle is the compiled application.
When you eventually distribute the app through the iTunes App Store, the application has a digital signature that validates the contents of the app. This signature is generated at compile time.
If you try to change the bundle after compilation, the app changes and the digital signature is no longer valid. This invalidates the application — who knows what code is in there, right? — and so Apple has set up iOS to throw an error if you try to modify the application.
Instead of writing to the bundle, your app can write to one of three accepted app-specific folders: <Application_Home>/Documents, <Application_Home>/tmp and <Application_Home>/Library/Caches.
Most likely, you will want to write to the <Application_Home>/Documents folder.
These folders are only accessible to your app. No other app can access the contents of these folders. (Likewise, your app cannot access another app's folders.)
You can set up your app to allow the end user to manage access to file data through iTunes, via desktop file sharing support.
This is because you should never modify the bundle of your application at runtime. Instead, you should have a folder elsewhere where you can add resources.
EDIT:
The error you are seeing is most likely because you cannot write to the bundle.
I encounter the same problem, when using a Log library. Finally, it's path format problem. Check the dataPath format. If it is Case 1, it is valid. In my case, it's Case 2, so I failed to create directory.
// Case 1
/var/mobile/Containers/Data/Application/5FB2CD2D-91DC-4FB2-8D6F-06369C70BB4A/Library/Caches/AppLogs
// Case 2, invalid format
file://var/mobile/Containers/Data/Application/5FB2CD2D-91DC-4FB2-8D6F-06369C70BB4A/Library/Caches/AppLogs
If the dataPath has a prefix, ex: file://, it is invalid.
As for an instance of NSURL, path will return the string like case 1, and absolutePath will return the string like case 2.
I'm still not totally clear on the meaning of the 513 error in my case, but I was getting it when just trying to read an opened file URL using [NSFileHandle fileHandleForReadingFromURL:theUrl error:&err ].
I realized from this answer that with iOS 13, I now need to use startAccessingSecurityScopedResource to access external files that are opened in the app. When I wrapped my file calls as follows, then the error 513 stopped occurring:
if( [myURL startAccessingSecurityScopedResource] )
{
NSFileHandle* myFile = [NSFileHandle fileHandleForReadingFromURL:myURL error:&err ];
// ...Do file reads here...
[theUrl stopAccessingSecurityScopedResource];
}
I have a project that uses a static library (SL). In that SL, there are a couple of strings I'd like to localize and the project includes all of the localization files. The localization works just fine when storing all text translations in the same file. The thing is that I'd like to separate the SL strings from the other strings. I have tried to put two different *.strings files (Localizable.strings and Localizable2.strings) in the language folder of interest but that did not work. I have also tried to use two *.strings file with the same name (Localizable.strings) but with different paths. It didn't work either. It seems that only one localization file is supported, right? Could anyone suggest a good way of doing this? I'm using SDK 3.2 beta 2.
It is not possible to bundle it in a static lib, but you can create new bundle like "MyStaticLibraryName.bundle", put inside all localizations and use the code below instead "NSLocalizedString()". All you need to do: add a static library and resource bundle.
NSString *MyLocalizedString(NSString* key, NSString* comment) {
static NSBundle* bundle = nil;
if (!bundle) {
NSString* path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"MyStaticLibraryName.bundle"];
bundle = [[NSBundle bundleWithPath:path] retain];
}
return [bundle localizedStringForKey:key value:key table:nil];
}
Putting files with the same name intro one project never works, because in the resulting app they end up all in the same location. (Xcode doesn't preserve your directory structure.)
But you can put part of your localization into Localizable2.strings and then use:
NSLocalizedStringFromTable(#"key", #"Localizable2", #"")
Make the localizable string for the static library, then place that string file in a folder "YourLibraryResource".
Rename the folder "YourLibraryResource.bundle".
Now you include this bundle also in the project along with the library. Then use the code given by abuharsky.