NSCoding and NSKeyedArchiving - "The document could not be loaded" - cocoa

I have implemented the NSCoding protocol for my classes, and I am using the following code in my NSDocument subclass to save and load:
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
[[record window] endEditingFor:nil];
return [NSKeyedArchiver archivedDataWithRootObject:self];
}
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError{
#try {
NSLog(#"Loading...");
self = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
#catch (NSException *exception) {
if (outError) {
NSDictionary *d = [NSDictionary dictionaryWithObject:#"The data is corrupted" forKey:NSLocalizedFailureReasonErrorKey];
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:d];
}
}
NSLog(#"whiteMoves count: %ld",[whiteMoves count]);
}
It all seems to be working fine. I can save a file and when I open it and step through the code it all seems to unarchive and decode fine. However, on completion of the 'load event' my application always pops up an error window that says "The document "xxx.xxx" could not be loaded." I cannot find where this error is being triggered from nor any documentation on it.
Does any one know where it comes from or know where it might be documented?
Thanks
Lee

You don't appear to be returning YES or NO from readFromData:
- (BOOL)readFromData:(NSData *)data
ofType:(NSString *)typeName
error:(NSError **)outError
{
BOOL retval = YES;
#try
{
NSLog(#"Loading...");
self = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"whiteMoves count: %ld",[whiteMoves count]);
}
#catch (NSException *exception)
{
if (outError != nil)
{
NSDictionary *d = [NSDictionary dictionaryWithObject:#"The data is corrupted" forKey:NSLocalizedFailureReasonErrorKey];
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:d];
}
retval = NO;
}
return retval;
}
This looks chess-related; can I ask what you are writing?

Related

Crashing in MFMailComposeViewController implementation

My app crashes when I try to perform sent or cancel button actions in MFMailComposeViewController.
here is my code.
I have imported message framework and added delegates in .h file.
NSString *emailTitle = #"Test Email";
NSString *messageBody = #"Welcome Guest";
NSArray *recipentsArray = [[NSArray alloc]initWithObjects:#"xxx#gmail.com", nil];
[[UINavigationBar appearance] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundColor:nil];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:NO];
[mc setToRecipients:recipentsArray];
[self presentViewController:mc animated:YES completion:NULL];(void) mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail sent failure: %#", [error localizedDescription]);
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:NULL];
}
This is how my mf mail composer looks like :----
Just make the MFMailComposeViewController *mc in global.
I mean declare it outside this method. It's crashing because at the end of method Your mc gets deallocated.
Interface
#interface Demo()
{
MFMailComposeViewController *mc;
}
#end
Implementation
-(void)ShareViaEmail {
objMailComposeController = [[MFMailComposeViewController alloc] init];
objMailComposeController.mailComposeDelegate = self;
if ([MFMailComposeViewController canSendMail]) {
[objMailComposeController setSubject:#"Hello"];
[objMailComposeController setMessageBody:#"This is Body of Message" isHTML:NO];
NSData *ImageData = [NSData dataWithContentsOfFile:self.aStrSourceName];
NSString *mimeType;
if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"Image"]) {
mimeType = #"image/png";
}
else if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"PDF"]) {
mimeType = #"application/pdf";
} else if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"Audio"]) {
mimeType = #"audio/mpeg";
} else if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"Video"]) {
mimeType = #"video/mp4";
}
[objMailComposeController addAttachmentData:ImageData mimeType:mimeType fileName:#"attachement"];
}
[self.navigationController presentViewController:objMailComposeController animated:YES completion:Nil];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
switch (result) {
case MFMailComposeResultCancelled:
[self ShowAlertView:kMsgMailCancelled];
break;
case MFMailComposeResultFailed:
[self ShowAlertView:kMsgMailFailed];
break;
case MFMailComposeResultSaved:
[self ShowAlertView:kMsgMailSaved];
break;
case MFMailComposeResultSent:
[self ShowAlertView:kMsgSent];
break;
default:
break;
}
// CHECK OUT THIS, THIS MIGHT BE IN YOUR CASE
[self.navigationController dismissViewControllerAnimated:controller completion:nil];
}

NSURLConnection with blocks

I'm using
[NSURLConnection connectionWithRequest:req delegate:self];
and then I use
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
-(void)connectionDidFinishLoading:(NSURLConnection *)connection;
to handle data loading. Everything is ok and working fine but I don't like the beauty of this code )
I wish to use blocks, to make my code looks like this:
[myConnection sendData:data
successBlock:^(void){NSLog(#"success");}
errorBlock:^(NSError * error){NSLog(#"error.description: %#", error.description);}];
is it possible to use NSURLConnection with blocks?
I use this class:
The MyConnection.h
#import <Foundation/Foundation.h>
#interface MyConnection : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
NSURLConnection * internalConnection;
NSMutableData * container;
}
-(id)initWithRequest:(NSURLRequest *)req;
#property (nonatomic,copy)NSURLConnection * internalConnection;
#property (nonatomic,copy)NSURLRequest *request;
#property (nonatomic,copy)void (^completitionBlock) (id obj, NSError * err);
-(void)start;
#end
And the MyConnection.m
#import "MyConnection.h"
static NSMutableArray *sharedConnectionList = nil;
#implementation MyConnection
#synthesize request,completitionBlock,internalConnection;
-(id)initWithRequest:(NSURLRequest *)req {
self = [super init];
if (self) {
[self setRequest:req];
}
return self;
}
-(void)start {
container = [[NSMutableData alloc]init];
internalConnection = [[NSURLConnection alloc]initWithRequest:[self request] delegate:self startImmediately:YES];
if(!sharedConnectionList)
sharedConnectionList = [[NSMutableArray alloc] init];
[sharedConnectionList addObject:self];
}
#pragma mark NSURLConnectionDelegate methods
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[container appendData:data];
}
//If finish, return the data and the error nil
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
if([self completitionBlock])
[self completitionBlock](container,nil);
[sharedConnectionList removeObject:self];
}
//If fail, return nil and an error
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if([self completitionBlock])
[self completitionBlock](nil,error);
[sharedConnectionList removeObject:self];
}
#end
to use it:
MyConnection * connection = [[MyConnection alloc]initWithRequest:req];
[connection setCompletitionBlock:^(id obj, NSError *err) {
if (!err) {
//It's ok, do domething with the response data (obj)
} else {
//There was an error
}
}];
[connection start];
It's based on the code, The Big Nerd Ranch uses on his book.
I hope it will be helpful.
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(#"%#", response);
NSLog(#"%#", data);
}];

Drag & drop (<NSDraggingDestination>) in Cocoa does not work

I want to implement some "simple" drag & drop into my Mac application.
I dragged in a view and entered "MyView" in my nib file accordingly.However I do not get any response in my console (I try to log messages whenever any of the protocol methods are triggered)
I have subclassed NSView like this:
#interface MyView : NSView <NSDraggingDestination>{
NSImageView* imageView;
}
and implement it like this:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes:[NSImage imagePasteboardTypes]];
NSRect rect = NSMakeRect(150, 0, 400, 300);
imageView = [[NSImageView alloc] initWithFrame:rect];
[imageView setImageScaling:NSScaleNone];
[imageView setImage:[NSImage imageNamed:#"test.png"]];
[self addSubview:imageView];
}
return self;
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
NSLog(#"entered");
return NSDragOperationCopy;
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender{
return NSDragOperationCopy;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender{
NSLog(#"exited");
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender{
NSLog(#"som");
return YES;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSURLPboardType] ) {
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
NSLog(#"%#", fileURL);
}
return YES;
}
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender{
NSLog(#"conclude sth");
}
- (void)draggingEnded:(id <NSDraggingInfo>)sender{
NSLog(#"ended");
}
- (BOOL)wantsPeriodicDraggingUpdates{
NSLog(#"wants updates");
}
- (void)updateDraggingItemsForDrag:(id <NSDraggingInfo>)sender NS_AVAILABLE_MAC(10_7){
}
If anyone still needs this, using:
[self registerForDraggedTypes:[NSArray arrayWithObjects:
NSFilenamesPboardType, nil]];
instead of:
[self registerForDraggedTypes:
[NSImage imagePasteboardTypes]]; // BAD: dropped image runs away, no draggingdestination protocol methods sent...
solved this problem for me. I suspect this works because my XIB is targeting osX 10.5. NSFilenamesPboardType represents a 10.5-and-previous 'standard data' UTI and [NSImage imagePasteboardTypes] returns 10.6-and-after standard data UTIs.
By the way, in - (BOOL)performDragOperation:(id NSDraggingInfo)sender, you can get a path for the file that was dropped with:
NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
Got the solution here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DragandDrop/Tasks/acceptingdrags.html#//apple_ref/doc/uid/20000993-BABHHIHC

When I use NSRunLoop Instruments gave a leak

When I use the following code I was told there is a leak:
- (void)dealloc
{
[connection release], connection = nil;
[responseData release],responseData = nil;
[cityCode release], cityCode = nil;
[requestUrlString release], requestUrlString = nil;
[returnDataDic release], returnDataDic = nil;
[super dealloc];
}
- (id)initWithCityCode:(NSString *)aCityCode
requestURL:(NSString*)urlString
responseType:(SWEngineRequestType)theResponsetype
target:(id)theTarget
action:(SEL)theAction
{
if ((self = [super init]))
{
_isExecuting = NO;
_isFinished = NO;
target = theTarget;
action = theAction;
cityCode = [aCityCode retain];
requestUrlString = [urlString copy];
responseType = theResponsetype;
returnDataDic = [[NSMutableDictionary alloc] initWithCapacity:1];
if (cityCode)
{
[returnDataDic setObject:cityCode forKey:SWEATHER_CITYCODE];
}
[returnDataDic setObject:[NSNumber numberWithInt:responseType] forKey:SWEATHER_DOWNTYPE];
}
return self;
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)finish
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
[connection release], connection = nil;
[responseData release],responseData = nil;
[cityCode release], cityCode = nil;
[requestUrlString release], requestUrlString = nil;
[returnDataDic release], returnDataDic = nil;
done = YES;
}
- (BOOL)isExecuting
{
return _isExecuting;
}
- (BOOL)isFinished
{
return _isFinished;
}
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
done = NO;
if ([self isCancelled])
{
[self willChangeValueForKey:#"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[pool release];
return;
}
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURL * urlToDownLoad = [NSURL URLWithString:[requestUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:urlToDownLoad cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection)
{
responseData = [[NSMutableData alloc] init];
[connection start];
}
else
{
[self finish];
}
if (connection != nil)
{
do
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
while (!done);
}
[pool release], pool = nil;
}
#pragma mark -
#pragma mark - NSURLConnectionDataDelegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[returnDataDic setObject:#"error" forKey:...];
[target performSelectorOnMainThread:action withObject:returnDataDic waitUntilDone:NO];
[self finish];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[returnDataDic setObject:responseData forKey:...];
[target performSelectorOnMainThread:action withObject:returnDataDic waitUntilDone:NO];
[self finish];
}
#end
the instrument gave me a leak at: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; Why? Thanks!
I just want to have a Asynchronous download at a operation but I use the NSAutoreleasePool then the instrument gave a leak at the:[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];.
Try putting your
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
in an autorelease pool.
What object did Instruments identify as having leaked? Where was it allocated? What was the stack trace?
When you run a run loop, all of the run loop sources and run loop observers may fire. So those few lines of code hide a near infinite set of possible things happening as the run loop runs. Any one of those may have a leak, or Instruments may be mistaken.
It is usually a bad idea to run the run loop in the default mode in an inner loop. It's not clear what you're trying to do with that loop, but generally you should schedule any of your own run loop sources in a private run loop mode and then run the run loop in that mode. That way, you're sure that only your own sources get run, which is usually what you want.

Problem with NSDocument and writeToURL:ofType:error:

I'm working on a document-based application, and I want to use a document package as my file format. To do that, it seems that the NSDocument method I need to override is
-writeToURL:ofType:error:.
It sometimes works, but only under certain conditions. For example, this code works:
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
{
NSFileWrapper *wrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
[wrapper addRegularFileWithContents:[#"please work" dataUsingEncoding:NSUTF8StringEncoding] preferredFilename:#"foobar"];
[wrapper writeToURL:absoluteURL options:NSFileWrapperWritingAtomic originalContentsURL:nil error:outError];
NSDictionary *metadata = [NSDictionary dictionaryWithObject:#"0.1" forKey:#"Version"];
NSURL *mdURL = [NSURL fileURLWithPath:[[absoluteURL path] stringByAppendingPathComponent:#"SiteInfo.plist"]];
[metadata writeToURL:mdURL atomically:YES];
return YES;
}
But, this code does not (it's the same as above, but with the NSFileWrapper bit taken out):
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
{
NSDictionary *metadata = [NSDictionary dictionaryWithObject:#"0.1" forKey:#"Version"];
NSURL *mdURL = [NSURL fileURLWithPath:[[absoluteURL path] stringByAppendingPathComponent:#"SiteInfo.plist"]];
[metadata writeToURL:mdURL atomically:YES];
return YES;
}
The above code puts this cryptic error into the console ("Lithograph" is the name of my app, and ".site" is the package extension):
NSDocument could not delete the temporary item at file://localhost/private/var/folders/qX/qXL705byGmC9LN8FpiVjgk+++TI/TemporaryItems/(A%20Document%20Being%20Saved%20By%20Lithograph%207)/Untitled%20Site.site. Here's the error:
Error Domain=NSCocoaErrorDomain Code=4 UserInfo=0x10059d160 "“Untitled Site.site” couldn’t be removed."
Do I have to write something to the original URL before I can add other files to the package?
If you are creating a file wrapper from your document, you should use -fileWrapperOfType:error: instead of -writeToURL:ofType:error:.
You would construct a file wrapper for the Info.plist file, insert it into the folder file wrapper and then return the folder wrapper:
- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError
{
NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil] autorelease];
[wrapper addRegularFileWithContents:[#"please work" dataUsingEncoding:NSUTF8StringEncoding] preferredFilename:#"foobar"];
NSDictionary *metadata = [NSDictionary dictionaryWithObject:#"0.1" forKey:#"Version"];
NSString* errorDescription = nil;
NSData* dictionaryData = [NSPropertyListSerialization dataFromPropertyList:metadata format:NSPropertyListBinaryFormat_v1_0 errorDescription:&errorDescription];
if(!dictionaryData)
{
if(!errorDescription)
errorDescription = #"Unknown error";
if(outError)
*outError = [NSError errorWithDomain:#"YourErrorDomain" code:69 userInfo:[NSDictionary dictionaryWithObject:errorDescription forKey:NSLocalizedDescriptionKey]];
return nil;
}
[wrapper addRegularFileWithContents:dictionaryData preferredFilename:#"Info.plist"];
return wrapper;
}
I was able to solve my own problem! By adding this line of code at the top of the method, I'm able to manipulate whatever files I want in the package:
[[NSFileManager defaultManager] createDirectoryAtPath:[absoluteURL path] withIntermediateDirectories:YES attributes:nil error:nil];
writeToURL... method implementations should always create a complete document. Your second version doesn't seem to do that, so I'm not sure why you think it should work.

Resources