NMSFTP File Download Works on Simulator, not on iPhone - ios

I am trying to use the SFTP library in NMSSH to download files from an ftp server and have been having trouble on my actual device. It works fine in the simulator. When I check for the file's existence in the stored directory (a created folder in my app's cache directory), the check passes on the simulator but fails on the phone. Below is the code I'm using. As a note, MainPath is the directory path and the fileListLevel1 row item is the file name with the extension.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = paths[0];
cacheDirectory = [#[cacheDirectory, #"/NewDirectory", #"/"] componentsJoinedByString:#""];
[[NSFileManager defaultManager] createDirectoryAtPath:cacheDirectory withIntermediateDirectories:false attributes:nil error: nil];
NSString *filePathToOpen = [#[MainPath, [fileListLevel1 objectAtIndex:indexPath.row]] componentsJoinedByString:#""];
bool isFile = NO;
bool isDir = NO;
isFile = [session.sftp fileExistsAtPath:filePathToOpen];
isDir = [session.sftp directoryExistsAtPath:filePathToOpen];
if (isFile){
NSString *fileToOpenNameAndPath = [#[cacheDirectory, [fileListLevel1 objectAtIndex:indexPath.row]] componentsJoinedByString:#""];
[session.sftp writeFileAtPath:filePathToOpen toFileAtPath:fileToOpenNameAndPath];
//FILE NOT FOUND ON THE PHONE - FOUND IN SIMULATOR!!!!!!!!
if ([[NSFileManager defaultManager] fileExistsAtPath:fileToOpenNameAndPath]){
NSLog(#"Found you!");
}
else if(![[NSFileManager defaultManager] fileExistsAtPath:fileToOpenNameAndPath]){
NSLog(#"Why you no there?");
}
I also tried creating the paths using the stringByAppendingPathComponent method and got the same result.
I'm new to app writing and objective c so I may be missing something fundamental here but I've been unable to find anything on this.
One additional question revolves around using the writeFileAtPath:toFileAtPath:progress command. I wonder if my issue is with the simulator writing much faster so my check happens before it is done on the phone. I'm not sure how to properly use the :progress portion as I'm unsure what type of variable the (BOOL (^)(NSUInteger sent)) is referring to or how to create one to check the progress.
Any help would be very much appreciated.

Related

Files saved in <Application_home>/Documents are being lost after a short period of time

I've got an app which saves images (JPEGs) and text files in the app's documents directory. I have tested it in the xcode simulator and on two iPhones (4 and 5, running ios 7.1 and 8.1 respectively), and it works exactly as expected, and the data is preserved fine.
However after submitting it to the app store a user testing the app (using iphone 6, ios 8.1) has found that the saved data is being lost every 20 minutes or so. Does anyone know why this is and how I can solve the issue?
Would marking the files as Do Not Backup solve the issue?
For reference, data being saved in NSUserDefaults is being preserved.
Thanks in advance.
EDIT----
I should have mentioned that I am searching for the documents directory by using:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
self.docsPath = [paths objectAtIndex:0];
I am then appending self.docsPath with the string attributed to the image/file, XXXX.jpg:
self.imgPath = [self.fileName stringByAppendingString:#".jpg"];
self.tempPhotoPath = [self.docsPath stringByAppendingPathComponent:self.imgPath];
I should also clarify that the app functions normally on the iPhone 4/5, unplugged from xcode and running appstore downloaded versions of the app. So far in investigating, it is specifically the iPhone 6 that I am having the problems with, and from what I can tell it is simply clearing the documents directory every 20 minutes or so.
in Apple new documents, whenever app launches every time it generate new sandbox id. So, it you have saved image with full path then it will might lost in second app launch. It will be not showing this effect if you test on simulator or device connected to xcode. Just plug out and run ur app, you can also see this issue your self.
So, instead of save image with its path, just create a folder in Document directory and save your image at there. For path generate you can write below code: (But make sure you save image with a specific id, like imageID and then fetch same image from that imageID).
- (NSString *)documentsPathForFileName:(NSString *)name folder:(NSString*)folderName{
return [[self pathToPatientPhotoFolder:folderName] stringByAppendingPathComponent:name];
}
- (NSString *)pathToPatientPhotoFolder:(NSString *)folderName {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) lastObject];
NSString *patientPhotoFolder = [documentsDirectory stringByAppendingPathComponent:folderName];
// Create the folder if necessary
BOOL isDir = NO;
NSFileManager *fileManager = [[NSFileManager alloc] init];
if (![fileManager fileExistsAtPath:patientPhotoFolder
isDirectory:&isDir] && isDir == NO) {
[fileManager createDirectoryAtPath:patientPhotoFolder
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
return patientPhotoFolder;
}
NSURL *urla = [NSURL URLWithString:[NSString stringWithFormat:#"%#",[self.imagearray objectAtIndex:indexPath.item]]];// imagearray for array of image url from server
NSString *thumbnailCacheKey = [NSString stringWithFormat:#"thumb-%#",[self.imageIDarray objectAtIndex:indexPath.item]];//first try to check thumb-<my image id> is exist or not.
UIImage *image = [UIImage imageWithContentsOfFile:[self documentsPathForFileName:thumbnailCacheKey folder:#"thumb"]];//this will check thumb-<my image id> in ur sandbox
if (!image){
UIImage *imageToSave = //
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:#"thumb"];
// New Folder is your folder name
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath])
[[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:&error];
NSString *filePath = [stringPath stringByAppendingPathComponent:thumbnailCacheKey];
NSData *pngData = UIImageJPEGRepresentation(image, 0.8);
[pngData writeToFile:filePath atomically:YES];
}
else{
yourimageView.image = image;
}

Where to store large files fetched initially in a IOS App

My app downloads at start a sqlite database about 300MB. After the download is done, this sqlite database should be available during the installtime of the app (years :-)) . I don't know where to store this files.
I started to store the data in the library path as follow (swift)
var sDataPath = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true);
No I realize that this path changes during development and testing with the IOS Simulator. Example:
1. I start the app the first time in OIS Simulator. The Evaluated Path is
[/Users/user/Library/Developer/CoreSimulator/Devices/19E2CB7E-3ABB-4C0A-8B49-39A0BE392A93/data/Containers/Data/Application/5EE51B55-0A89-45FB-A1E2-9BE3DCD33463/Library]
2. The App downloads the data. This takes some minutes.
3. I close the simulator and restart also xcode
4. I start the app again in the simulator and expect to have the already downloaded data in my apps' library path. But no ...
5. the app evaluates a new path, which has another ID in the Path:
[/Users/user/Library/Developer/CoreSimulator/Devices/19E2CB7E-3ABB-4C0A-8B49-39A0BE392A93/data/Containers/Data/Application/429206E9-00EA-45EF-BE6E-4B2E9374BAF5/Library]
6. And the app downloads the content again.
I would like to have a static path over the lifetime of the device. What am I doing wrong or what should I change ?
The path is changing on the simulator but it will be the same on a real device.
If I'm not wrong, on the simulator the path change every time you restart it. On a device the path will change only if you delete and reinstall your application.
You should save downloaded image to document directory. In this way images can load fastly. If u want to overcome of reloading cell then you have to check for then file path whether is fill is there or not.
- (NSString *)documentsPathForFileName:(NSString *)name folder:(NSString*)folderName{
return [[self pathToPatientPhotoFolder:folderName] stringByAppendingPathComponent:name];
}
- (NSString *)pathToPatientPhotoFolder:(NSString *)folderName {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) lastObject];
NSString *patientPhotoFolder = [documentsDirectory stringByAppendingPathComponent:folderName];
// Create the folder if necessary
BOOL isDir = NO;
NSFileManager *fileManager = [[NSFileManager alloc] init];
if (![fileManager fileExistsAtPath:patientPhotoFolder
isDirectory:&isDir] && isDir == NO) {
[fileManager createDirectoryAtPath:patientPhotoFolder
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
return patientPhotoFolder;
}
in your cellForRowAtIndexPath: method
{…..
NSURL *urla = [NSURL URLWithString:[NSString stringWithFormat:#"%#",[self.imagearray objectAtIndex:indexPath.item]]];
NSString *thumbnailCacheKey = [NSString stringWithFormat:#"thumb-%#",[self.imageIDarray objectAtIndex:indexPath.item]];;
UIImage *image = [UIImage imageWithContentsOfFile:[self documentsPathForFileName:thumbnailCacheKey folder:#"thumb"]];
if (!image){
//first download image here and then save that image like:
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:#"thumb"];
// New Folder is your folder name
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath])
[[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:&error];
NSString *filePath = [stringPath stringByAppendingPathComponent:thumbnailCacheKey];
NSData *pngData = UIImageJPEGRepresentation(image, 0.8);
[pngData writeToFile:filePath atomically:YES];
}
else{// image is already downloaded, so just set that image
cell.imageView.image = image;
}
….
}
This code creates folder in your DocumentDirectory and save image there and fetch from there. I save with one unique photoID so that image can not be same. You can save with your requirement.
You should store it on Cache folder.
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *cacheFile = [cachesPath stringByAppendingPathComponent:#"file.plist"];
Store large file in other directory will be rejected by apple app review team (as i have faced)

Xcode 6 keeps renaming my app's directory in iOS8 simulator after each run.

I'm running Xcode 6 Beta 5 but this has been happening since the first beta. My app's directory in the simulator keeps being renamed after each run. It took me a while to figure this out. I'm using this to get the doc's dir reference.
NSString *folder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) lastObject];
NSLog(#"Documents Dir: %#",folder);
Now for example on the first run it'll be:
/Users/Joey/Library/Developer/CoreSimulator/Devices/5B9930EE-A9B4-4B36-BABB-AA864ACAF2DE/data/Containers/Data/Application/4B10C2E4-A5C3-4C64-93B1-4069FCCB9C46/Documents
Second run now it's:
/Users/Joey/Library/Developer/CoreSimulator/Devices/5B9930EE-A9B4-4B36-BABB-AA864ACAF2DE/data/Containers/Data/Application/7E9EB62D-115A-4092-AD23-CB6BA3E5E10F/Documents
Third run:
/Users/Joey/Library/Developer/CoreSimulator/Devices/5B9930EE-A9B4-4B36-BABB-AA864ACAF2DE/data/Containers/Data/Application/EC8F41E8-52ED-4B10-9808-B3ACC46FC6AA/Documents
This is wreaking havoc with my app because it stores path references for certain files within the app. It's not that my NSLog statement is returning incorrect results, I verified this is what happening in Finder. It's changing the name every time. Has anyone seen this happen? Is this a "feature" that I'm misunderstanding?
Turns out Xcode 6 does in fact change the app's UUID every run, and I'm in the wrong for storing absolute paths.
USE SIMPHOLDERS
I used this app on Xcode 5 opens the Documents folder, for the currently running app in the simulator, in Finder.
http://simpholders.com/
not ready for Xcode 6 yet (as of sep 24 2014) but saves all this hassle.
In Xcode 6 / iOS8 The bundle is now separate from the data./ The application GUID is regenerated between runs in Xcode (not sure why)
DOCUMENTS DIR:/Users/gbxc/Library/Developer/CoreSimulator/Devices/AC79941F-EC56-495E-A077-773EEE882732/data/Containers/Data/Application/C220D351-0BE7-46BA-B35E-D16646C61A3F/Documents
mainBundlePath_:/Users/gbxc/Library/Developer/CoreSimulator/Devices/AC79941F-EC56-495E-A077-773EEE882732/data/Containers/Bundle/Application/12200D1D-9B67-408B-BCF7-38206CBE0940/myappname.app/BLANK_BLOG_SCALED.jpg
1. FIND THE DEVICES FOLDER in SIMULATOR
/Users/gbxc/Library/Developer/CoreSimulator/Devices/
open each /device.plist to see which GUID is which device in XCode - I think this is static
3. FIND THE DEVICE you're running on iPad 2 - I think this is static
/Devices/AC79941F-EC56-495E-A077-773EEE882732
4. Find your application /Documents folder
/AC79941F-EC56-495E-A077-773EEE882732/data/Containers/Data/Application/C220D351-0BE7-46BA-B35E-D16646C61A3F/Documents
BEWARE the GUID C220D351-0BE7-46BA-B35E-D16646C61A3F is regenerated everytime the app is run in XCode 6
NSArray *paths_ = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if(paths_){
_docsDir = [paths_ firstObject];
DebugLog(#"DOCUMENTS DIR:%#",_docsDir);
}else{
ErrorLog(#"paths_ is nil - cant get Documents directory");
}
MAIN BUNDLE path
NSString *mainBundlePath_ = [[NSBundle mainBundle] pathForResource:#"someimageinyourbundle" ofType:#"jpg"];
/AC79941F-EC56-495E-A077-773EEE882732/data/Containers/Bundle/Application/12200D1D-9B67-408B-BCF7-38206CBE0940/clarksonsiq.app/BLANK_BLOG_SCALED.jpg
NEVER CACHE THE PATH to /Documents between runs it will change.
I was serializing it to a plist and couldnt figure out why they kept disappearing
The GUID above /Documents keeps changing between runs but if you have /Documents open in Finder the folder stays open.
https://devforums.apple.com/thread/235911?tstart=0
https://devforums.apple.com/thread/238754?tstart=0
Free Solution
Use Open Source Library OpenSim. OpenSim is an open source alternative for SimPholders, written in Swift.
Paid Solution
Use SimPholder application to know current application location.
For xcode 6.0 >
Download SimPholder 2.0 alpha 2
For xcode 5.1 <
Download SimPholders 1.5
I can confirm that this is Xcode 6 related not iOS 8.
I have two development machines. On one of them I have Xcode 5. I was working all the time on that machine and my URL's were fine (photo app, photos are visible).
Yesterday I checked in form git my source on a machine with Xcode 6. I noticed that my photos are not visible any more, only photos that are created during that app session.
After little debugging, I realized that file:///var/mobile/Applications/B6A6BAEF-C90C-4A2A-93DB-E6700B88971F/Documents/ is changing on every app run.
All that time I am working with iOS 7 device.
I am going to check once more on a machine with Xcode 5 to confirm when I get my hands on it.
You need to to save only path inside DocumentDirectory(directory/file name), and add it to the DocumentDirectory every time you load the file...
-(void)saveImage:(UIImage *)image{
NSData *pngData = UIImagePNGRepresentation(image);
NSString *pathInDocumentDirectory = [APP_DocumentDirectory stringByAppendingPathComponent:PROFILE_IMAGE_NAME];
NSString *filePath = [self documentsPathForFileName:pathInDocumentDirectory];
//Save pic file path - DirName/Filename.png
[XYZPreferencesHelper setUserImageFilePath:pathInDocumentDirectory];
//Write the file
[[NSFileManager defaultManager] createFileAtPath:filePath contents:pngData attributes:nil];
}
-(void)loadSavedUserPicture{
//Load saved DirName/Filename.png
NSString *pathInDocumentDirectory = [XYZPreferencesHelper getUserImageFilePath];
if (pathInDocumentDirectory != nil){
//Full path with new app Document Directory
NSString *filePath = [self documentsPathForFileName:pathInDocumentDirectory];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
NSData *pngData = [NSData dataWithContentsOfFile:filePath];
UIImage *image = [UIImage imageWithData:pngData];
if (image != nil){
userPicImageView.image = image;
}
}
}
}
- (NSString *)documentsPathForFileName:(NSString *)name
{
NSString *documentsPath = [self createRestcallDirectoryIfNotExist];
return [documentsPath stringByAppendingPathComponent:name];
}
-(NSString *)createRestcallDirectoryIfNotExist{
NSString *path;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
path = [documentsPath stringByAppendingPathComponent:APP_DocumentDirectory];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) //Does directory already exist?
{
if (![[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:NO
attributes:nil
error:&error])
{
NSLog(#"Create directory error: %#", error);
}
}
return documentsPath;
}
Because app's UUID changes and not reliable 
so we should not store urls instead we should store just file names and reconstruct url at runtime , on update/reinstall iOS creates new home directory, stores app bundle in it and copies documents files so url changes


NSKeyedArchiver not persisting data

So, my app queries an Amazon Dynamo DB database and retrieves a few kilobytes worth of data. What I want the app to do is download everything the first time, and then every time after, just download a timestamp to see if it has the most recent version of the data. So that I only have to download the data every once in a while, I'm trying to use NSKeyedArchiver to archive the array that I'm downloading. I have tried this three different ways, and none of them work on an iPhone, although two of them work on the simulator.
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:#"dataArray.archive"];
This does not work on the simulator nor the actual iphone. The result of this method is NO.
The next thing I used was the full path:
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:#"Users/Corey/Desktop/.../dataArray.archive"];
And this worked on the simulator, but not on the iPhone. My guess was that when compiled, the filesystem looks different (and obviously doesn't have the same path). So next I tried:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"dataArray" ofType:#".archive"];
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:filePath];
Once again, this works on the simulator but fails on the iphone. I have confirmed that all of the data is in self.dataArray before writing to the archive, and confirmed that the array is nil after writing back to the archive (in the iphone version). Any ideas what's going on? Is there a better way to do the filepath?
This is what I tracked down:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent: #"dataArray.archive"];
[NSKeyedArchiver archiveRootObject:your_object toFile:filePath];
and it worked perfectly on both the simulator and the iPhone!
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:#"dataArray.archive"];
You have to provide a full path.
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:#"Users/Corey/Desktop/.../dataArray.archive"];
That is not a full path. A full path begins with / and does not have /../ anywhere.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"dataArray" ofType:#".archive"];
You do not have permission to write inside the mainBundle, it is read only.
Also, in general you shouldn't use file paths, you should use URLs. Some APIs (including this one) requires a path but URLs are the recommended approach these days.
Here's the proper way to write the file to disk:
NSURL *applicationSupportUrl = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask][0];
applicationSupportUrl = [applicationSupportUrl URLByAppendingPathComponent:#"My App"]; // replace with your app name
if (![applicationSupportUrl checkResourceIsReachableAndReturnError:NULL]) {
[[NSFileManager defaultManager] createDirectoryAtURL:applicationSupportUrl withIntermediateDirectories:YES attributes:#{} error:NULL];
}
NSURL *archiveUrl = [applicationSupportUrl URLByAppendingPathComponent:#"foo.archive"];
[NSKeyedArchiver archiveRootObject:self.dataArray toFile:archiveUrl.path];

Where should I put the XML file in iPhone Simulator's filesystem. XCode 4.2

A little embarrassing question, but I can find an answer which works in my case... I need to put some xml file (settings.xml) in order to read some data from it during application runtime.
According to some answers here and not only here, I have putted it here:
~/Library/Application Support/iPhone Simulator/5.0/[AppUUID]/Documents
and I'm trying to use it as follows:
// Loading data from external XML File
NSURL *url = [[NSBundle mainBundle]
URLForResource: #"settings" withExtension:#"xml"];
NSError *err;
if ([url checkResourceIsReachableAndReturnError:&err] == NO){
NSLog(#"FILE NOT FOUND");
}
Result: "FILE NOT FOUND".
I've tried to do put the file under any possible directory in
~/Library/Application Support/iPhone Simulator/5.0/[AppUUID]/ and efect is still the same.
I'm using XCode 4.2
If you are putting the file into the .../Documents folder then you need to use the following code to access it (you are looking for it in the App Bundle, which is a different location altogether):
NSString *docFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filename = [docFolder stringByAppendingPathComponent:#"settings.xml"];
if ([[NSFileManager defaultManager] fileExistsAtPath:filename])
{
// Read file
}
else
{
NSLog(#"settings.xml file not found!");
}

Resources