We are using the SSZipArchive method createZipFileAtPath:withFilesAtPaths: in order to compress a single log file, which is then uploaded to Amazon S3. All files are located in our mobile app's NSTemporaryDirectory().
Sometimes, when I download the resulting zip files from Amazon to my mac and double-click them, I get the following error:
Archive Utility
Unable to expand "[file name].zip" into "[folder name]".
(Error 1 - Operation not permitted.)
When this happens, I always notice that the file size is 22 bytes. This seems too small. When the files behave OK they are usually 1 mb or more.
Here is the relevant code from our app:
-(void) sendConsoleLog
{
//NSError * error;
//NSString * sTemp = [[NSString alloc] initWithContentsOfFile:[self logFilePath] encoding:NSUTF8StringEncoding error:&error];
[SSZipArchive createZipFileAtPath:[self compressedLogFilePath] withFilesAtPaths:#[[self logFilePath]]];
[self upload];
}
-(void) upload
{
_sDestinationFileName = [NSString stringWithFormat:#"logs/%#.zip", [[NSUUID UUID] UUIDString]];
AWSS3TransferUtilityUploadExpression *expression = [AWSS3TransferUtilityUploadExpression new];
expression.progressBlock = self.progressBlock;
[expression setValue:#"public-read" forRequestHeader:#"x-amz-acl"];
AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
NSString * sFileURL = [NSString stringWithFormat:#"file://%#", [self compressedLogFilePath]];
[[transferUtility uploadFile:[NSURL URLWithString:sFileURL]
bucket:S3_BUCKET_NAME
key:_sDestinationFileName
contentType:#"application/octet-stream"
expression:expression
completionHander:self.completionHandler] continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"cclChatSendLog continueWithBlock Error: %#", task.error);
}
if (task.exception) {
NSLog(#"cclChatSendLog continueWithBlock Exception: %#", task.exception);
}
if (task.result) {
AWSS3TransferUtilityUploadTask *uploadTask = task.result;
NSUInteger iTaskID = uploadTask.taskIdentifier;
NSLog(#"cclChatSendLog.h %lu continueWithBlock: Uploading...", (unsigned long)iTaskID);
}
return nil;
}];
}
-(NSString*) logFilePath
{
NSString *fileName =[NSString stringWithFormat:#"console%lu.log",(unsigned long)(appDelegate.hash)];
return [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
}
-(NSString*) compressedLogFilePath
{
return [NSString stringWithFormat:#"%#.zip", [self logFilePath]];
}
I wonder if the 22 bytes zip is created in case there is no file found in [self logFilePath]. Has anyone encountered a similar behaviour?
Related
My app has turned on Data Protection and I created a file with NSFileProtectionComplete
+ (void)createLogFile {
NSString *deviceModel = [Utils getDeviceModel];
NSString *appVersion = [Utils getAppVersion];
NSData *initData = [[NSString stringWithFormat:#"%#-%#\n================================\n\n\n", deviceModel, appVersion] dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:[self logFilePath]
contents:initData
attributes:#{NSFileProtectionKey: NSFileProtectionComplete}];
}
and when I lock my device applicationProtectedDataWillBecomeUnavailable: will be called.
- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSData *key = [MyKeychain getKey];
NSString *log = [NSString stringWithFormat:#"The key is:\n %#", key];
[MyFileLogger logInfo:log];
});
}
Then I can find the result in the file, which means I was able to write that file when my device is locked.
Shouldn't Data Protection prevents from accessing files when device is locked? What's wrong?
--updated-- (add method logInfo:)
+ (void)logInfo:(NSString *)str {
NSString *info = [self wrapWithTimestamp: str];
NSString *logFilePath = [Utils logFilePath];
if (![[NSFileManager defaultManager] fileExistsAtPath:logFilePath]) {
[Utils createLogFile];
}
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:[info dataUsingEncoding:NSUTF8StringEncoding]];
[handle closeFile];
}
According to the answer to this question, after the applicationProtectedDataWillBecomeUnavailable method is called there is a 10 second "grace period" before data protection activates.
If you increase your time delay from 5 to 11 seconds you should see that your data is not written to your log file.
I was able to observe this with sample code and an 11 second delay.
I'm developing an app that can receive messages/files while it's on the background via webRTC.
When a file is received, I write it to disk. But when trying to access it later (even between app launches) that file doesn't exist.
User's document folder has NSFileProtectionCompleteUntilFirstUserAuthentication attribute.
I've tried to create the file on disk using NSData's [writeToURL:options:error:], [writeToFile:options:error:]; NSFileManager's [createFileAtPath:contents:attributes:] and also NSFileHandle's methods.
All of them successfully create the file at the designated path/url. Right after creation I check whether file exists with NSFileManager's [attributesOfItemAtPath:error:] which shows me the following:
attributes: {
NSFileCreationDate = "2018-05-07 18:47:50 +0000";
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 501;
NSFileGroupOwnerAccountName = mobile;
NSFileModificationDate = "2018-05-07 18:47:50 +0000";
NSFileOwnerAccountID = 501;
NSFileOwnerAccountName = mobile;
NSFilePosixPermissions = 420;
NSFileProtectionKey = NSFileProtectionCompleteUntilFirstUserAuthentication;
NSFileReferenceCount = 1;
NSFileSize = 92156;
NSFileSystemFileNumber = 695101;
NSFileSystemNumber = 16777219;
NSFileType = NSFileTypeRegular;
}
[[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]] also shows me that the file exists after write.
Considering it could be a threading problem, I've also tried to write that file putting it on a main thread block, but result is the same. First file seems to be written but, when trying to access it afterwards it's like it never was.
Is there anything I could be missing?
edit: Added function I use to write.
- (void) saveFileData:(NSData *)fileData completionHandler:(void(^)(BOOL success))completionHandler {
NSURL *fileURL = [self fileURL];
NSError *error = nil;
[fileData writeToURL:fileURL options:NSDataWritingAtomic error:&error];
if (error) {
ZLogError(ZLogTypeFile, #"[%#] could not be saved: %#", self.fileKey, error);
completionHandler(NO);
return;
}
ZLogDebug(ZLogTypeFile, #"<file: %#> exists after write:%d", fileURL, [[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]);
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:&error];
ZLogDebug(ZLogTypeFile, #"attributes: %#", attributes);
completionHandler(YES);
}
output comes as (where Documents is the users NSDocumentDirectory in the app)
[file: /Documents/57/Downloads/Images/9d1687ab5f4374a2c00429a24316b5ccd3fb0a67.png] exists after write:1
and getting the file (an image):
- (UIImage *) imageFromURL:(NSURL *)imageURL {
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[imageURL path]];
if (!fileExists) {
ZLogDebug(ZLogTypeFile, #"[file: %#] exists: %d", imageURL, fileExists);
return nil;
}
return [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
}
in log (note I've taken out the long path before Documents directory just here):
[file: /Documents/57/Downloads/Images/9d1687ab5f4374a2c00429a24316b5ccd3fb0a67.png] exists: 1
I am trying to download only image and text(probably HTML string) of a Evernote's note in my iOS app. I have successfully downloaded image from a note . But I did not find any method or process which help me to get text which are written on the note . I have used
ENSDK.framework
-(void)findAllNotes {
NSLog(#"finding all notes..");
[self.session findNotesWithSearch:nil
inNotebook:nil
orScope:ENSessionSearchScopeAll
sortOrder:ENSessionSortOrderNormal
maxResults:255
completion:^(NSArray* findNotesResults,
NSError* findNotesError) {
if (findNotesError) {
[self.session unauthenticate];
NSAssert(NO, #"Could not find notes with error %#", findNotesError);
} else {
[self processFindNotesResults:findNotesResults];
}
}];
}
- (void)processFindNotesResults:(NSArray*)results {
NSParameterAssert(results);
NSLog(#"processing find notes results..");
for (ENSessionFindNotesResult* result in results) {
[self.session downloadNote:result.noteRef
progress:NULL
completion:^(ENNote* note,
NSError* downloadNoteError) {
NSAssert(!downloadNoteError, #"Could not download note with error %#",
downloadNoteError);
[self getDataFromNote:note];
}];
}
}
-(void)getDataFromNote:(ENNote*)note {
for (ENResource* resource in note.resources) {
if ([resource.mimeType hasPrefix:#"image"]) {
UIImage* image = [[UIImage alloc] initWithData:resource.data];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docs = [paths objectAtIndex:0];
NSString* path = [docs stringByAppendingFormat:#"/image1.jpg"];
NSData* imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, .8)];
NSError *writeError = nil;
if(![imageData writeToFile:path options:NSDataWritingAtomic error:&writeError]) {
NSLog(#"%#: Error saving image: %#", [self class], [writeError localizedDescription]);
}
}
}
}
The content of the note is available to you in the content property of your variable note; i.e. it's in the content property of an ENNote object.
Also note that in addition to accessing the content directly, the Evernote iOS SDK also includes a special method that makes it easy to display a note's content in a UIWebView:
We've made this easy-- rather than serializing it to HTML and fussing with attached image resources, we've provided a method to generate a single Safari "web archive" from the note; this is a bundled data type which UIWebView natively knows how to load directly.
In my host App I am downloading custom emojis images folder after unzipping successfully saving by below url.
NSURL* shareContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.company.app.PushServiceExtn"];
And without any issue whenever user tapping on emojis icon all the custom emojis shows in grid in place of keyboard by shareContainerURL.
I have created PushNotification Service Extension where I need to show the custom emojis image by fetching emoji name from payload whenever push comes. using below code.
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSDictionary* mediaAttachment = [self.bestAttemptContent.userInfo objectForKey:#"media-attachment"];
NSString* attachType = [mediaAttachment objectForKey:#"attachType"];
if ([attachType isEqualToString:#"emoji"]) {
NSString* strEmojiURL = [mediaAttachment objectForKey:#"url"];
self.bestAttemptContent.title = strEmojiURL;
NSString* emojiName = [[strEmojiURL stringByRemovingPercentEncoding] lastPathComponent];
NSString* strUnpresseedEmojiPath = [self getFullPath:#"emoji/Pressed"];
NSString* strImagePath = [NSString stringWithFormat:#"%#/%# Pressed.png",strUnpresseedEmojiPath, emojiName];
NSURL* fileURL = [NSURL fileURLWithPath:strImagePath];
NSData *imageData = [NSData dataWithContentsOfURL:fileURL];
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
NSError* error;
// CGRect rect = CGRectMake(0,0,50,50);
// #{UNNotificationAttachmentOptionsThumbnailClippingRectKey:(__bridge NSDictionary*)CGRectCreateDictionaryRepresentation(rect)} option dict;
UNNotificationAttachment * attachement = [UNNotificationAttachment attachmentWithIdentifier:strImagePath.lastPathComponent URL:fileURL options:nil error:&error];
if (error == nil) {
self.bestAttemptContent.attachments = #[attachement];
}
}
}
self.contentHandler(self.bestAttemptContent);
}
- (NSString *)getFullPath:(NSString *)file {
NSURL* shareContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.company.app.PushServiceExtn"];
return [shareContainerURL.path stringByAppendingPathComponent: file];
}
I am always getting valid url but second time I get image nil but first time of every image it works. Couldn't get the root cause. Any help would appreciated.
Below is the error that occurred second time for every image.
2016-10-27 17:34:59.081026 pushNotificationServiceExtension[651:34632] Attachement Error = Error Domain=UNErrorDomain Code=100 "Invalid attachment file URL" UserInfo={NSLocalizedDescription=Invalid attachment file URL}
Also please let me know how to view App Group shared container, Couldn't find way to view the files contained inside.
*Update = * File is getting deleted after showing in push notification.
From apple "UNNotificationAttachment Once validated, attached files are moved into the attachment data store so that they can be accessed by the appropriate processes. Attachments located inside an app’s bundle are copied instead of moved."
So I copy my emoji image to duplicate URL and assign it to UNNotificationAttachment.
if (imageFileURL) {
NSURL* duplicateImageURL = [self getFullPath:#"EmojiAttachment"];
if (![fileManager fileExistsAtPath:duplicateImageURL.path]) {
[fileManager createDirectoryAtPath:duplicateImageURL.path withIntermediateDirectories:NO attributes:nil error:&error];
}
emojiName = [NSString stringWithFormat:#"%# Unpressed.png", emojiName];
duplicateImageURL = [duplicateImageURL URLByAppendingPathComponent:emojiName];
[[NSFileManager defaultManager]copyItemAtURL:imageFileURL toURL:duplicateImageURL error:&error];
UNNotificationAttachment * attachement = [UNNotificationAttachment attachmentWithIdentifier:emojiName URL:[duplicateImageURL filePathURL] options:nil error:&error];
if (error == nil) {
self.bestAttemptContent.attachments = #[attachement];
}
else{
NSLog(#"Attachement Error = %#",error);
}
}
I'm trying to sync files created in my app to Dropbox, however it seems the syncing only happens after the app quits, and not in real time when files are created and moved between locations in different folders in the app or created/deleted. Is there a certain call I have to make for instance? Appreciate your help!
Below is the code I am using for syncing:
-(void)createFilePathinFolder:(NSString *)folderName FileName:(NSString *)fileName {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *folder = [self localDocumentsRootPath];
if (![folderName isEqualToString:#"root"]) {
folder = [folder stringByAppendingPathComponent:folderName];
}
NSString *file = [folder stringByAppendingPathComponent:fileName];
if (![fileManager fileExistsAtPath:file]) {
[fileManager createFileAtPath:file contents:[#"0" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
}
//Insert to FileTable
[[DBHelper shared]insertToFileTableWithFolder:folderName FileName:fileName MetaFileName:nil Tag:nil Title:nil];
if ([NetworkHelper shared].canSyncWithCloud) {
NSString *filePathStr = [folderName stringByAppendingPathComponent:fileName];;
if ([folderName isEqualToString:#"root"]) {
filePathStr = fileName;
}
DBPath *filePath = [[DBPath root] childPath:filePathStr];
DBError *error;
DBFile *destFile =[[DBFilesystem sharedFilesystem] createFile:filePath error:&error];
NSData *fileData = [NSData dataWithContentsOfFile:file];
[destFile writeData:fileData error:&error];
//[destFile writeContentsOfFile:file shouldSteal:NO error:&error];
[destFile close];
if (error) {
NSLog(#"Error when creating file %# in Dropbox, error description:%#", fileName, error.description);
}
}
}
Your error checking is all wrong. Your code should be more like this:
DBPath *filePath = [[DBPath root] childPath:filePathStr];
DBError *error = nil;
DBFile *destFile =[[DBFilesystem sharedFilesystem] createFile:filePath error:&error];
if (destFile) {
NSData *fileData = [NSData dataWithContentsOfFile:file];
if (![destFile writeData:fileData error:&error]) {
NSLog(#"Error when writing file %# in Dropbox, error description: %#", fileName, error);
}
[destFile close];
} else {
NSLog(#"Error when creating file %# in Dropbox, error description: %#", fileName, error);
}
The file should sync right away with the code that you have. This assumes you have properly linked your app to an account and all.
What version of the Dropbox Sync API are you using? 1.0.7 has some potential networking issues. I have a beta of 1.0.8 that seems to solve these issues. You may need to wait until 1.0.8 comes out.
You can verify if Dropbox is hung. While running your app in the debugger, wait a minute after the file has been created. If the file doesn't appear, pause your app in the debugger and look at all of the threads. You should see one or more dropbox related threads. If one looks blocked with a reference to dbx_cfhttp_request then you have hit a bug in the Dropbox framework. Putting your device in Airplane mode for 10-15 seconds then turning Airplane mode off again should kick it back into gear.