Getting 'unrecognised selector' from NSNotificationCenter - objective-c

I'm trying to implement an observer in the NSNotificationCenter. Instead of using self as the observer I want to create a little object that does it:
typedef void (^ErrorCallback)(NSError*);
typedef void (^SuccessCallback)();
typedef void (^ReplicationChanged) (NSNotification*);
#interface SyncParams : NSObject
#property (copy) ErrorCallback errorCallback;
#property (copy) SuccessCallback successCallback;
#property (copy) ReplicationChanged replicationChanged;//this used to observe
- (void)replicationChanged:(NSNotification*)notification;
#end
#implementation SyncParams
#end
Then later I create an instance of the observer:
SyncParams* params = [SyncParams alloc];
params.replicationChanged = ^(NSNotification* notification) {
//do stuff here
};
And finally add it to the NSNotificationCenter:
[[NSNotificationCenter defaultCenter] addObserver: params
selector: #selector(replicationChanged:)
name: kCBLReplicationChangeNotification
object: replicationObject];
But I get this error: Exception '-[SyncParams replicationChanged:]: unrecognized selector sent to instance 0x7ff945c049a0' was thrown
I'm very new to objective-c! Any pointers?

Yes and no, I ended up with the following:
[[NSNotificationCenter defaultCenter] addObserverForName:kCBLReplicationChangeNotification object:repl queue:nil usingBlock:^(NSNotification *notification) {
NSString *status;
if (repl.status == kCBLReplicationActive) {
NSLog(#"Repication in progress");
status = #"in-progrss";
} else if (repl.status == kCBLReplicationOffline) {
NSLog(#"Sync in offline");
status = #"offline";
} else if (repl.status == kCBLReplicationStopped) {
NSLog(#"Sync in stopped");
status = #"stopped";
} else if (repl.status == kCBLReplicationIdle) {
NSLog(#"Sync in idle");
status = #"idle";
}
NSError *error = repl.lastError;
if(error) {
status = #"error";
NSLog(#"replication error %#", error.code);
}
NSDictionary *dictionary = #{
#"type": type,
#"changesCount": #(repl.changesCount),
#"completedChangesCount": #(repl.completedChangesCount),
#"running": #(repl.running),
#"status": status,
#"suspended": #(repl.suspended),
};
[self sendEventWithName:#"replicationChanged" body:dictionary];
}];
I only ever get one callback though, when it starts up.

Related

NSURLSessionDownloadTask in NSOperation crashes on cancel

I'm trying to create a DownloadOperation subclass of NSOperation to download data asynchronously. Everything seemed to be working fine until I tried to add cancelling support. Basically, the completion handler of the operation's NSURLSessionDownloadTask seems to be called after the operation has been released. It will crash with EXC_BAD_ACCESS at the line weakSelf.state = kFinished.
The full sample project is here: https://github.com/angstsmurf/DownloadOperationQueue. Press Command+. after running to crash.
#import "DownloadOperation.h"
typedef enum OperationState : NSUInteger {
kReady,
kExecuting,
kFinished
} OperationState;
#interface DownloadOperation ()
#property NSURLSessionDownloadTask *task;
#property OperationState state;
#end
#implementation DownloadOperation
// default state is ready (when the operation is created)
#synthesize state = _state;
- (void)setState:(OperationState)state {
#synchronized(self) {
if (_state != state) {
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_state = state;
[self didChangeValueForKey: #"isExecuting"];
[self didChangeValueForKey: #"isFinished"];
}
}
}
- (OperationState)state {
#synchronized (self) {
return _state;
}
}
- (BOOL)isReady { return (self.state == kReady); }
- (BOOL)isExecuting { return (self.state == kExecuting); }
- (BOOL)isFinished { return (self.state == kFinished); }
- (BOOL)isAsynchronous {
return YES;
}
- (instancetype)initWithSession:(NSURLSession *)session downloadTaskURL:(NSURL *)downloadTaskURL completionHandler:(nullable void (^)(NSURL * _Nullable, NSURLResponse * _Nullable, NSError * _Nullable))completionHandler {
self = [super init];
if (self) {
__unsafe_unretained DownloadOperation *weakSelf = self;
// use weak self to prevent retain cycle
_task = [[NSURLSession sharedSession] downloadTaskWithURL:downloadTaskURL
completionHandler:^(NSURL * _Nullable localURL, NSURLResponse * _Nullable response, NSError * _Nullable error) {
/*
if there is a custom completionHandler defined,
pass the result gotten in downloadTask's completionHandler to the
custom completionHandler
*/
if (completionHandler) {
completionHandler(localURL, response, error);
}
/*
set the operation state to finished once
the download task is completed or have error
*/
weakSelf.state = kFinished;
}];
}
return self;
}
- (void)start {
/*
if the operation or queue got cancelled even
before the operation has started, set the
operation state to finished and return
*/
if (self.cancelled) {
self.state = kFinished;
return;
}
// set the state to executing
self.state = kExecuting;
NSLog(#"downloading %#", self.task.originalRequest.URL.absoluteString);
// start the downloading
[self.task resume];
}
-(void)cancel {
[super cancel];
// cancel the downloading
[self.task cancel];
}
#end
As pointed out in the comments by Scott Thompson, the correct keyword to use for the weakSelf variable is __weak, not __unsafe_unretained.

Memory Leak Using IOKit (Adapted From USBPrivateDataSample)

Well. I'm back with another memory leak question. I recently asked a similar question here, but it turns out that code doesn't entirely do what I am trying to accomplish (which is to perform an action whenever any USB device connects/disconnects, and not just when an HID devices does).
I've started over from scratch, and have adapted Apple's USBPrivateDataSample and some of GitHub project ORSSerialPort into something that seems to work just like I want it to -- except --there are memory leaks. :(
I've tried applying the knowledge gained from my previous posts about dealing with memory leaks, but I'm stuck again. What am I missing? Thanks very much in advance.
Here's the code:
**AppDelegate.h:**
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (strong) NSMutableDictionary *deviceDictionary;
#end
**AppDelegate.m:**
#import "AppDelegate.h"
#include <IOKit/usb/IOUSBLib.h>
typedef struct DeviceData
{
io_object_t notification;
CFStringRef deviceName;
CFStringRef serialNum;
} DeviceData;
static IONotificationPortRef gNotifyPort;
static io_iterator_t gAddedIter;
static CFRunLoopRef gRunLoop;
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.deviceDictionary = [[NSMutableDictionary alloc] init];
CFMutableDictionaryRef matchingDict;
CFRunLoopSourceRef runLoopSource;
kern_return_t kr;
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (matchingDict == NULL)
{
fprintf(stderr, "IOServiceMatching returned NULL.\n");
}
// Create a notification port and add its run loop event source to our run loop
// This is how async notifications get set up.
gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
gRunLoop = CFRunLoopGetCurrent();
CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);
// Now set up a notification to be called when a device is first matched by I/O Kit.
kr = IOServiceAddMatchingNotification(gNotifyPort, // notifyPort
kIOFirstMatchNotification, // notificationType
matchingDict, // matching
DeviceAdded, // callback
NULL, // refCon
&gAddedIter // notification
);
// Iterate once to get already-present devices and arm the notification
DeviceAdded(NULL, gAddedIter);
// Start the run loop. Now we'll receive notifications.
CFRunLoopRun();
// can't do these, causes EXC_BAD_ACCESS crash
//CFRelease(matchingDict);
//CFRelease(gNotifyPort);
//CFRelease(kr);
CFRelease(runLoopSource);
CFRelease(gRunLoop);
}
void DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
{
kern_return_t kr;
DeviceData *deviceDataRef = (DeviceData *) refCon;
if (messageType == kIOMessageServiceIsTerminated)
{
kr = IOObjectRelease(deviceDataRef->notification);
AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
if ((deviceDataRef->serialNum) && (deviceDataRef->deviceName))
{
// remove device from dict
[appDelegate updateDeviceDict:
(__bridge NSString *)deviceDataRef->serialNum:
(__bridge NSString *)deviceDataRef->deviceName:
NO];
}
free(deviceDataRef);
}
}
void DeviceAdded(void *refCon, io_iterator_t iterator)
{
kern_return_t kr;
io_service_t usbDevice;
while ((usbDevice = IOIteratorNext(iterator)))
{
io_name_t deviceName;
CFStringRef deviceNameCF;
DeviceData *deviceDataRef = NULL;
// Add some app-specific information about this device.
// Create a buffer to hold the data.
deviceDataRef = malloc(sizeof(DeviceData));
bzero(deviceDataRef, sizeof(DeviceData));
// Get the USB device's name.
kr = IORegistryEntryGetName(usbDevice, deviceName);
if (KERN_SUCCESS != kr)
{
deviceName[0] = '\0';
}
CFMutableDictionaryRef usbProperties = 0;
if (IORegistryEntryCreateCFProperties(usbDevice, &usbProperties, kCFAllocatorDefault, kNilOptions) != KERN_SUCCESS)
{
IOObjectRelease(usbDevice);
continue;
}
NSDictionary *properties = CFBridgingRelease(usbProperties);
NSString *serialNum = properties[(__bridge NSString *)CFSTR("USB Serial Number")];
NSNumber *builtIn = properties[(__bridge NSString *)CFSTR("Built-In")];
deviceNameCF = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII);
if (builtIn.integerValue != 1)
{
// Save the device's name to our private data.
deviceDataRef->deviceName = deviceNameCF;
deviceDataRef->serialNum = (__bridge CFStringRef)(serialNum);
AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
[appDelegate updateDeviceDict:
(__bridge NSString *)deviceDataRef->serialNum:
(__bridge NSString *)deviceDataRef->deviceName:
YES];
}
// Register for an interest notification of this device being removed. Use a reference to our
// private data as the refCon which will be passed to the notification callback.
kr = IOServiceAddInterestNotification(gNotifyPort, // notifyPort
usbDevice, // service
kIOGeneralInterest, // interestType
DeviceNotification, // callback
deviceDataRef, // refCon
&(deviceDataRef->notification) // notification
);
// Done with this USB device; release the reference added by IOIteratorNext
kr = IOObjectRelease(usbDevice);
}
}
- (void) updateDeviceDict : (NSString *) deviceSerial : (NSString *) deviceName : (bool) keepDevice
{
if (deviceName)
{
if ((!deviceSerial) || ([deviceSerial isEqualToString:#""])) deviceSerial = deviceName;
#try
{
//#throw [[NSException alloc] initWithName:#"test" reason:#"test" userInfo:nil];
// if the device needs to be added
if ((keepDevice) && (deviceSerial))
{
if (![self.deviceDictionary objectForKey:deviceSerial])
{
[self.deviceDictionary setObject:[NSDictionary dictionaryWithObjectsAndKeys:deviceName, #"name", nil] forKey:deviceSerial];
NSLog(#"Device added: %#", deviceName);
}
}
else // if the device needs to be removed
{
[self.deviceDictionary removeObjectForKey:deviceSerial];
NSLog(#"Device removed: %#", deviceName);
}
}
#catch (NSException *exception)
{
[self.deviceDictionary removeAllObjects];
CFRunLoopRun();
}
#finally
{
NSLog(#"\n%#", self.deviceDictionary);
}
}
else // name is nil
{
[self.deviceDictionary removeAllObjects];
CFRunLoopRun();
}
}

EXC_BAD_ACCESS for an object created inside a Block

I have always been nervous when it comes to blocks and GCD because my mind tells me that it looks very complex!
I am getting a crash inside a block which ideally looks alright to me:
#pragma mark -
-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
__weak VTVehicleServiceNetworkManager *weakSelf = self;
TaskBlock fetchOrdersListTaskBlock = ^()
{
__block __strong HLOrdersDataProvider *ordersDataProvider = nil;
NSBlockOperation *fetchOrdersOperation = [NSBlockOperation blockOperationWithBlock:[^{
ordersDataProvider = [[HLOrdersDataProvider alloc] init];
[ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
completionBlock:completionBlock
errorBlock:errorBlock];
} copy]];
[weakSelf.dataOperationQueue addOperation:fetchOrdersOperation];
};
[self fetchDataWithTaskBlock:[fetchOrdersListTaskBlock copy]
errorBlock:^(NSError *error) {
errorBlock(error);
}];
}
I was able to trace out the zombie object but I am not able to figure out why is this object turning out to be a zombie. Here is the snapshot from profile:
I have gone through the following guides (1, 2) to see if I can find out what I am doing wrong but I was no where near to find out what is going wrong.
Any help and reference text to what I am doing wrong will help.
Edit:
I have tried what #Jerimy has suggested and in fact my code which I have posted earlier was exactly the same as required: Declaring and initializing ordersDataProvider inside the block operation itself. But since it was crashing at the same point I tried to declare it outside the block just to see if it addresses the crash.
Below is the new code I tested:
#pragma mark -
-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
__weak VTVehicleServiceNetworkManager *weakSelf = self;
completionBlock = [completionBlock copy];
errorBlock = [errorBlock copy];
TaskBlock fetchOrdersListTaskBlock = ^()
{
NSBlockOperation *fetchOrdersOperation = [NSBlockOperation blockOperationWithBlock:^{
HLOrdersDataProvider *ordersDataProvider = [[HLOrdersDataProvider alloc] init];
[ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
completionBlock:completionBlock
errorBlock:errorBlock];
}];
[weakSelf.dataOperationQueue addOperation:fetchOrdersOperation];
};
[self fetchDataWithTaskBlock:[fetchOrdersListTaskBlock copy]
errorBlock:^(NSError *error) {
errorBlock(error);
}];
}
The crash from Profile:
There is not much from the stack trace as well, SDMHTTPRequest is a library and am very sure there is nothing wrong there, and the HLOrdersDataProvider is the zombie object which I was able to trace out in Instruments app:
EDIT 2
Adding the interface and implementation of HLOrdersDataProvider for more details:
#interface HLOrdersDataProvider : HLDataProvider
-(void)performFetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock;
#end
#implementation HLOrdersDataProvider
-(void)performFetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
// Using SDMConnectivity
NSString *queryString = [infoDict valueForKey:#"$filter"];
NSString *appendStringForEndpoint = [kRowsetsKeyword stringByAppendingFormat:#"?%#", queryString];
[self fetchDataFromServerWithEndPointAppendString:appendStringForEndpoint
completionBlock:completionBlock
errorBlock:errorBlock];
}
#pragma mark - Service Agent related
-(NSString*)collectionName
{
return [NSString stringWithString:kRowsetsKeyword];
}
-(void)requestFinished:(SDMHttpRequest *)request
{
NSError *error = nil;
// Let's parse the response and send the results back to the caller
NSString *collectionName = [self collectionName];
NSData *responseData = [request responseData];
NSArray *entitiesArray = [self parseODataEntriesWithData:responseData
withCollectionName:collectionName
error:&error];
if (error)
[self triggerFailureBlockWithArgument:error];
else
[self triggerCompletionBlockWithArgument:entitiesArray];
}
#end
Also, HLOrdersDataProvider is inherited from HLDataProvider so below is the interface and implementation of this class too:
#import <Foundation/Foundation.h>
//#import "SDMHttpRequestDelegate.h"
#import "SDMRequestBuilder.h"
#import "SDMHttpRequest.h"
#import "SDMParser.h"
#import "HLConstant.h"
#import "HLConnectionData.h"
#interface HLDataProvider : NSObject <SDMHttpRequestDelegate>
#property (copy, atomic) CompletionBlock completionBlock;
#property (copy , atomic) ErrorBlock errorBlock;
#property (copy, atomic) CompletionBlockWithDataFetchStatus completionBlockWithDataFetchStatus;
+ (id)sharedInstance;
- (NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName;
- (NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName error:(NSError**)outError;
- (NSMutableArray*)parseJSONEntriesWithData:(NSData*)data;
-(NSArray*)fetchEntriesFromDatabaseWithEntityName:(NSString*)entityName relationshipObjects:(NSMutableArray*)relationships;
-(void)updateDatabaseWithEntries:(NSMutableArray*)scanEntries;
-(void)updateDatabaseWithJSONEntries:(NSMutableArray*)scanEntries;
-(id)parsedOdataResultFromEntries:(NSMutableArray*)entries;
-(void)fetchDataFromServerWithEndPointAppendString:(NSString*)appendStr completionBlock:(CompletionBlock)inCompletionBlock errorBlock:(ErrorBlock)inErrorBlock;
-(NSString*)collectionName;
-(void)triggerCompletionBlockWithArgument:(id)inArg;
-(void)triggerFailureBlockWithArgument:(NSError*)inArg;
#end
#implementation HLDataProvider
+ (id)sharedInstance
{
//Subclassess will override this method
return nil;
}
-(NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName
{
return [self parseODataEntriesWithData:data
withCollectionName:collectionName
error:NULL];
}
-(NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName error:(NSError**)outError
{
NSMutableArray *entriesArray = nil;
#try {
entriesArray = sdmParseODataEntriesXML(data,
[[[HLConnectionData metaDataDocument] getCollectionByName:collectionName] getEntitySchema],
[HLConnectionData serviceDocument]);
}
#catch (NSException *exception) {
NSLog(#"Got exception: %#", exception);
if (outError)
{
*outError = [NSError errorWithDomain:#"Vehicle Service"
code:-1001
userInfo:[NSDictionary dictionaryWithObject:exception forKey:NSLocalizedDescriptionKey]];
}
}
#finally {
}
return entriesArray;
}
- (NSMutableArray*)parseJSONEntriesWithData:(NSData*)data
{
NSError *error = nil;
id object = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&error];
NSMutableArray *resultArray = nil;
if(error) { /* JSON was malformed, act appropriately here */ }
if([object isKindOfClass:[NSDictionary class]])
{
resultArray = [NSMutableArray arrayWithObject:object];
}
else if ([object isKindOfClass:[NSArray class]])
{
resultArray = [NSMutableArray arrayWithArray:object];
}
return resultArray;
}
#pragma mark -
#pragma mark - Data Fetch - Server - SDMConnectivity
-(void)fetchDataFromServerWithEndPointAppendString:(NSString*)appendStr completionBlock:(CompletionBlock)inCompletionBlock errorBlock:(ErrorBlock)inErrorBlock;
{
self.errorBlock = inErrorBlock;
self.completionBlock = inCompletionBlock;
id<SDMRequesting> request = nil;
//NSString *clientStr = #"&sap-client=320&sap-language=EN";
NSString *urlStr =[NSString stringWithFormat:#"%#/%#",[HLConnectionData applicationEndPoint], appendStr];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[SDMRequestBuilder setRequestType:HTTPRequestType];
request=[SDMRequestBuilder requestWithURL:[NSURL URLWithString:urlStr]];
[request setUsername:kUserName];
/*Set Password in SDMRequesting object*/
[request setPassword:kPassword];
[request setRequestMethod:#"GET"];
[request setTimeOutSeconds:kTimeoutInterval];
/*set the Delegate. This class must adhere to SDMHttpRequestDelegate to get the callback*/
[request setDelegate:self];
/*Call startAsynchronous API to request object to retreive Data asynchrnously in the call backs */
[request startSynchronous];
}
-(void)updateDatabaseWithEntries:(NSMutableArray*)scanEntries
{
//Subclasses will override this
}
-(void)updateDatabaseWithJSONEntries:(NSMutableArray*)scanEntries
{
//Subclasses will override this
}
-(id)parsedOdataResultFromEntries:(NSMutableArray*)entries
{
//Subclasses will override this
return nil;
}
-(void)deleteExistingEntriesFromCoredata
{
//Subclasses will override this
}
-(NSArray*)fetchEntriesFromDatabaseWithEntityName:(NSString*)entityName relationshipObjects:(NSMutableArray*)array
{
//Subclasses will overide this method
return nil;
}
#pragma mark - SDMHTTPRequestDelegate methods
- (void)requestStarted:(SDMHttpRequest*) request
{
}
- (void)requestFinished:(SDMHttpRequest*) request
{
// For service doc and metadata we instantiate HLDataProvider, so we send this raw SDMHTTPRequest object as-is. For other service agents like HLOrdersDataProvider we send the parsed information, check the subclass' implementation of -requestFinished: method
[self triggerCompletionBlockWithArgument:request];
}
-(void)triggerCompletionBlockWithArgument:(id)inArg
{
self.completionBlock(inArg);
}
- (void)requestFailed:(SDMHttpRequest*) request
{
[self triggerFailureBlockWithArgument:request.error];
}
-(void)triggerFailureBlockWithArgument:(NSError*)inArg
{
self.errorBlock(inArg);
}
- (void)requestRedirected:(SDMHttpRequest*) request
{
}
#pragma mark - Service Agent related
-(NSString*)collectionName
{
// Should be overridden by the subclasses
return nil;
}
The referenced code is very convoluted (you're doing things in a very complicated way).
First off, you should not be creating the copy of your blocks in the caller. Make the copy in the callee if necessary (ie: to copy it to heap instead of using the stack-allocated block if you are going to call it after the stack has been popped). In almost all APIs using blocks, it is not the caller's responsibility to ensure that a block is on heap.
There is no reason for your ordersDataProvider variable to be declared in the scope of fetchOrdersListTaskBlock because it is only ever used inside of fetchOrdersOperation's block.
I don't immediately see the cause of your crash, but I suspect that simplifying your code will help reveal the problem. Perhaps the issue is in HLOrdersDataProvider's initializer.
Try something like:
-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
completionBlock = [completionBlock copy];
errorBlock = [errorBlock copy];
__weak VTVehicleServiceNetworkManager *weakSelf = self;
TaskBlock fetchOrdersListTaskBlock = ^{
NSBlockOperation *fetchOrdersOperation = [NSBlockOperation blockOperationWithBlock:^{
HLOrdersDataProvider *ordersDataProvider = [[HLOrdersDataProvider alloc] init];
[ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
completionBlock:completionBlock
errorBlock:errorBlock];
}];
[weakSelf.dataOperationQueue addOperation:fetchOrdersOperation];
};
[self fetchDataWithTaskBlock:fetchOrdersListTaskBlock
errorBlock:errorBlock];
}
Or better yet, re-design your class to work like this (I don't see a need for NSBlockOperation in your example):
-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
completionBlock = [completionBlock copy];
errorBlock = [errorBlock copy];
TaskBlock fetchOrdersListTaskBlock = ^{
HLOrdersDataProvider *ordersDataProvider = [[HLOrdersDataProvider alloc] init];
[ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
completionBlock:completionBlock
errorBlock:errorBlock];
};
[self fetchDataWithTaskBlock:fetchOrdersListTaskBlock
errorBlock:errorBlock];
}

TouchDB saves the attachment but does not save the property

I found that my CouchModel (TSCAssetUploadDO) saves the attachment but does not save the property.
TSCAssetUploadDO *assetDO = [[TSCAssetUploadDO alloc] initWithNewDocumentInDatabase: _localAssetUploadDatabase];
NSData *data = UIImageJPEGRepresentation(contact.avatar, 1.0); // may need to resize.
NSString *attachmentName = [docID stringByAppendingString:#".jpg"];
[assetDO createAttachmentWithName:attachmentName type:#"image/jpeg" body:data];
assetDO.relatedDocID = docID;
assetDO.docType = #"contact";
RESTOperation *op2 = [assetDO save];
//[op2 wait]; originally thought that making it sync may work
[op2 onCompletion:^{
if (op2.error) NSLog(#"ERROR [TOUCHDB] %#", op2.error);
else
{
NSLog(#"YES! it saved");
}
if (completionBlock)
{
completionBlock(op2.error, contact);
}
//[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationDataUpdated object:nil];
}];
This is resulting to a document like this ( with saved attachment, no property like docType and relatedDocID though )
{
"_id": "D50ED630-34ED-4A02-A9C8-204E79A0648B",
"_rev": "1-b0ce9eaa1fb2f86dc9ae619e27ffe1ea",
"_attachments": {
"QSPNGC665.jpg": {
"content_type": "image/jpeg",
"revpos": 1,
"digest": "md5-u9V0rgoSRN5cUW2T3xh0hw==",
"length": 117500,
"stub": true
}
}
}
Below is the CouchModel that I just used.
#interface TSCAssetUploadDO: CouchModel
#property(nonatomic,retain) NSString *relatedDocID;
#property(nonatomic,retain) NSString *docType;//entity or contact
#property bool *toProcess;
#property (retain) NSDate* created_at;
#end
#implementation TSCAssetUploadDO
- (NSDictionary*) propertiesToSave {
// Initialize created_at the first time the document is saved:
if (self.created_at == nil)
self.created_at = [NSDate date];
return [super propertiesToSave];
}
#end
Is there anything that I did wrong?
Solved my issue. need to add #dynamic
#implementation TSCAssetUploadDO
#dynamic relatedDocID, docType, toProcess, created_at;
<... rest of the code >
#end

NSNotification arrived with broken object

i'm posting notification in this manner:
...
IVSession *newSession = [[[IVSession alloc] initWithDictionary:propertyDict] autorelease];
NSDictionary *notifParams = [NSDictionary dictionaryWithObject:newSession forKey:#"session"];
NSNotification *newSessionNotif = [NSNotification notificationWithName:IVNewSessionNotificaiton object:self userInfo:notifParams];
...
IVSession interface:
#interface IVSession : IVMappableObject {
NSString *_ssid;
NSNumber *_uid;
}
#property (nonatomic,retain) NSString *sessionID;
#property (nonatomic,retain) NSNumber *userID;
and init method:
- (id)initWithDictionary:(NSDictionary*)dict
{
if ((self = [super init]))
{
NSDictionary *mapping = [self elementToPropertyMappings];
for (NSString *key in mapping)
[self setValue:[dict objectForKey:key] forKey:[mapping objectForKey:key]];
}
return self;
}
but at the method, called for this notification, i'm receiving broken newSession object - its properties ssid and uid are invalid summaries:
-(void)didOpenSession:(NSNotification *)newSession
{
if (receivedSession)
[receivedSession release];
receivedSession = [[newSession userInfo] objectForKey:#"session"];
}
where is my fault?
code looks ok to me... have you verified that the IVSession object contains what you expect it to, after assembling things, but prior to posting the notification?