I have an application that works with the server.
I would like to make an offline mode that is when a user creates something or changes it was saved somewhere and then when an internet connection appears - these requests go to the server. How can I achieve this? How to store blocks in NSUserDefaults? How not to lose pointers to these blocks?
It can be different controllers. Help me please. I apologize for my bad English.
I've tried to save this objects in NSUserDefaults
typedef void (^ExecutionBlock)(void);
#interface OfflineBlockObject : NSObject
#property (nonatomic, copy) ExecutionBlock block;
#property (nonatomic, strong) NSArray<NSMutableArray *> *operandsArray;
#property (nonatomic, strong) NSArray *conditionsArray;
#end
OfflineBlockObject *blockObject = [[OfflineBlockObject alloc] init];
BOOL first = [self.reservation.reservationID boolValue];
NSArray *conditions = #[#(first), #(self.shouldCallSetTagsForReservation)];
NSArray *operands = #[#[[self.reservation json], self.reservation.reservationID ? : #0, self.selectedTags ? : #[]],
#[[self.reservation json], #(self.shouldForceApproved), self.selectedTags? : #[]]];
blockObject.conditionsArray = [conditions copy];
blockObject.operandsArray = [operands copy];
#weakify(blockObject);
blockObject.block = [^{
#strongify(blockObject);
if ([blockObject.conditionsArray[0] boolValue]) {
ReservationsModel *m = [[ReservationsModel alloc] init];
[m editReservation:blockObject.operandsArray[0][0] success:^(id responseObject) {
if ([blockObject.conditionsArray[1] boolValue]) {
[m setReservationTags:blockObject.operandsArray[0][1] tags:blockObject.operandsArray[0][2] success:nil failure:nil];
}
} failure:nil];
} else {
self.reservation.eventId = self.reservationEvent.eventInfoID;
ReservationsModel *m = [[ReservationsModel alloc] init];
[m createReservation:blockObject.operandsArray[1][0] shouldForceApproved:[blockObject.operandsArray[1][1] boolValue] success:^(id responseObject) {
Reservation *reservation = [Reservation reservationWithJson:responseObject];
if (reservation.reservationID) {
if ([blockObject.conditionsArray[1] boolValue]) {
[m setReservationTags:reservation.reservationID tags:blockObject.operandsArray[0][2] success:nil failure:nil];
}
}
} failure:nil];
}
} copy];
[[OfflineQueueHelper sharedHelper] addTask:blockObject];
But my block property becomes null :(
So I have a pod that does pretty much this, but it's in Swift (the Obj-C made the interface too convoluted) - https://cocoapods.org/pods/OfflineRequestManager. You can make any object that conforms to OfflineRequest that wraps whatever network request you want to make it to the server. You can also provide a dictionary that gets written to disk if you want to make sure that it persists through app termination. We've been using it in our internal apps for a while now, so hopefully somebody else can get some use out of it.
The simplest use case would look something like the following, though most actual cases (saving to disk, specific request data, etc.) will have a few more hoops to jump through:
import OfflineRequestManager
class SimpleRequest: OfflineRequest {
func perform(completion: #escaping (Error?) -> Void) {
doMyNetworkRequest(withCompletion: { response, error in
handleResponse(response)
completion(error)
})
}
}
///////
OfflineRequestManager.defaultManager(queueRequest: SimpleRequest())
Related
My app currently uses this deprecated function:
id unarchivedObject=[NSKeyedUnarchiver unarchiveObjectWithData:codedData];
if([unarchivedObject isKindOfClass:[NSDictionary class]]){
// currently returns TRUE when reading existing user data.
}
To update, I've converted to this:
id unarchivedObject=[NSKeyedUnarchiver unarchivedObjectOfClass:[NSDictionary class] fromData:codedData error:nil];
if([unarchivedObject isKindOfClass:[NSDictionary class]]){
// currently returns FALSE when reading existing user data.
}
The data was originally encoded like this:
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:text forKey:#"text"];
}
-(instancetype)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
text=[decoder decodeObjectForKey:#"text"];
}
What could be causing the IF statement to return FALSE using the newer code?
Please note that I am concerned primarily with reading existing data stored prior to deprecating the Archiving functions. Simply changing to the newer functions does not resolve the issue.
Interesting question! I've been supporting iOS 10.0 so I haven't encountered such issue until I saw this. I was tinkering for an hour and I successfully found the issue.
What could be causing the IF statement to return FALSE using the newer
code?
It's because your unarchivedObject object is nil!
If you use the parameter error in the new method, you would see an error like this:
Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only
decode classes that adopt NSSecureCoding. Class 'QTPerson' does not
adopt it." UserInfo={NSDebugDescription=This decoder will only decode
classes that adopt NSSecureCoding. Class 'QTPerson' does not adopt it.
But how do we get the correct value for this unarchivedObject and not nil? It would take a couple of steps.
First off, make your model/class conform to <NSCoding, NSSecureCoding>
Example:
QTPerson.h
#import <Foundation/Foundation.h>
#class QTPerson;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - Object interfaces
#interface QTPerson : NSObject <NSCoding, NSSecureCoding>
#property (nonatomic, copy) NSString *text;
#end
NS_ASSUME_NONNULL_END
And then implement the protocol methods:
QTPerson.m
#import "QTPerson.h"
#implementation QTPerson
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:_text forKey:#"text"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_text = [coder decodeObjectOfClass:[NSString class] forKey:#"text"];
}
return self;
}
#end
And then when archiving an object, you would want to pass YES to the parameter requiringSecureCoding, like so:
QTPerson *person = [[QTPerson alloc] init];
person.text = #"Glenn";
NSData *codedData1 = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:nil];
[[NSUserDefaults standardUserDefaults] setValue:codedData1 forKey:#"boom"];
Lastly, when unarchiving, just do what you did correctly, like so:
NSData *codedData = [[NSUserDefaults standardUserDefaults] dataForKey:#"boom"];
NSError *er;
id unarchivedObject=[NSKeyedUnarchiver unarchivedObjectOfClass:[QTPerson class] fromData:codedData error:&er];
if([unarchivedObject isKindOfClass:[QTPerson class]]){
NSLog(#"TRUE!");
} else {
NSLog(#"FALSE!");
}
Voila! You'll get nonnull object unarchivedObject, hence the TRUE/YES value you're looking for!
In all of my iOS application I use this approach to respect MVC, I want to be sure that my implementation is correct and respects the best practices and the MVC design pattern :
Singleton of AFNetworking acting as API for network calls:
MyAPI.h :
#import "AFHTTPSessionManager.h"
#import "AFNetworking.h"
#interface MyAPI : AFHTTPSessionManager
+(MyAPI *)sharedInstance;
#end
MyAPI.m :
#pragma mark - Singleton
+(MyAPI*)sharedInstance
{
static MyAPI *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyAPI alloc] initWithBaseURL:[NSURL URLWithString:kROOT_URL]];
});
return sharedInstance;
}
Model User that uses the singleton to fecth the data of user (is that good as implementation ?):
User.h
#interface User : NSObject
#property (strong,nonatomic) NSString *userId;
#property (strong,nonatomic) NSString *email;
#property (strong,nonatomic) NSString *password;
-(id) initWithDictionary: (NSDictionary *) dictionay;
+(BOOL) isConnected;
+(void) disconnect;
+(NSString *) idOfConnectedUser;
+(User *) connectedUser;
+(void) loginWith : (NSString *) email andPassword :(NSString *) password complete:(void(^)(id result, NSError *error))block;
+(void) searchUsersFrom : (NSString *) countryCode withName :(NSString *) name andLevel:(NSString *) levelCode complete: (void(^)(id result, NSError *error)) block;
+(void) signup:(void(^)(id result, NSError *error)) block;
+(void) getUserFriends:(void(^)(id result, NSError *error)) block;
#end
User.m
[......]
+(void) loginWith : (NSString *) email andPassword :(NSString *) password complete: (void(^)(id result, NSError *error)) block
{
__block NSString * result ;
NSDictionary *params = #{#"email": email, #"password": password};
[[MyAPI sharedInstance] POST:#"auth/" parameters:params success:^(NSURLSessionDataTask *task, id responseObject)
{
if([responseObject objectForKey:#"id"])
{
[[NSUserDefaults standardUserDefaults] setObject:(NSDictionary*) responseObject forKey:USER_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
result = [responseObject objectForKey:#"id"];
}
else
{
result = nil ;
}
if (block) block(result, nil);
} failure:^(NSURLSessionDataTask *task, NSError *error)
{
if (block) block(nil, error);
}];
}
[.....]
LoginController.m :
-(void)loginButtonAction:(UIButton *)sender
{
[......]
[ User loginWith:text andPassword:text complete:^(id result, NSError *error)
{
if (result)
{
[APPDELEGATE start];
}
else
{
// ERROR
}
}];
}
So does my implementation respects the MCV and follows the best practices and how can I improve it if not ?
Singletons: You might want to avoid using singletons, it'll help you to improve your API design and make code more testable. Also, in case of User, imagine you will want to support changing user (logout/guest user/etc). With current approach, you will be limited to sending a NSNotification because everyone who uses connectedUser can not know that underlying reference has changed.
ActiveRecord:
What you did with your model User that is capable of performing networking is somewhat similar to active record approach which might not scale so well when you model becomes more complicated and the number of actions it can perform increases. Consider separating those into pure model and services that actually perform networking (or whatever else you will need in the future).
Model Serialisation:
Consider encapsulating model & network response serialisation logic into a separate class (e.g. LoginResponse that among other things points to a User) frameworks like Mantle make it much easier.
MVC: from my experience in iOS MVC might not be the most optimal approach for anything but simple apps. With MVC tendency is to put put all the logic into your ViewController making it very big and hard to maintain. Consider other patterns such as MVVM
All in all I understand that it is hard to learn all the new technologies at once, but you can definitely start by making sure each class performs one thing and one thing only: Model does not do networking or persisting to the disk, API client doesn't deserialise each response or saves data to NSUserDefaults, view controller doesn't do anything except for listening to user events (button taps etc). This alone would make your code much easier to reason about and to follow if a new developer would be introduced to your codebase.
Hope it helps!
I dont have anything to say about your MVC(Model–view–controller) correct?
I just want to add something that may be useful approach avoiding unwanted crashes..
First is under
[[MyAPI sharedInstance] POST:#"auth/" parameters:params success:^(NSURLSessionDataTask *task, id responseObject)
{
if([responseObject objectForKey:#"id"])
{
[[NSUserDefaults standardUserDefaults] setObject:(NSDictionary*) responseObject forKey:USER_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
result = [responseObject objectForKey:#"id"];
}
else
{
result = nil ;
}
}];
it is always possible for so many reason to mention that the reponseObject could be nil and therefore object dont have the key #"id" and will lead to error(crash in worst case). i have this code i dont know if this can be considered as best practice but here it is:
if ([responseObject isKindOfClass:[NSArray class]])
{
NSLog(#"Log: Response is of class NSArray");
}
else if ([responseObject isKindOfClass:[NSDictionary class]])
{
NSLog(#"Log: Response is of class NSDictionary");
}
else
{
NSLog(#"Log: Kind of class is not supported");
}
This example restricts other kind of class especially [NSNull class]
Second in line:
NSDictionary *params = #{#"email": email, #"password": password};
by checking email and password first before assigning to NSDictionary, setting nil to NSDictionary will cause crash.
Third is line:
if (block) block(result, nil);
block returns void from your implementation. Is this working? Sorry for asking i haven't tried if-statement with a block like this..
complete: (void(^)(id result, NSError *error)) block
void in this is the returning value of your block, or i'm wrong.. hmm..
if (block) only checks if the block block exist, so intead of checking it (which we are sure that is existing)..
maybe you want to check the result instead...
if (result != nil) block(result, nil); is the right statement
the reason behind is:
if (result != nil) // will return NONE nil value only
{
block(result, nil);
}
// else will not set things to the block
//or maybe just
block(result, nil); // which will allow the 'block(nil, nil);' and under your implementation
[ User loginWith:text andPassword:text complete:^(id result, NSError *error)
{
if (result)
{
[APPDELEGATE start];
}
else if (result == nil & error == nil)
{
// NO objectForKey #"id"
}
else
{
// ERROR
}
}];
while under failure:^(NSURLSessionDataTask *task, NSError *error) simply block(nil, error);
I am having a lot of trouble wrapping my head around the best way to use blocks. I am trying to retrieve pedometer data, and the method of accessing the data is a block...
[self.pedometer queryPedometerDataFromDate:yesterday
toDate:midnightOfToday
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"Pedometer is NOT available.");
}
else {
NSLog(#"Steps %#", pedometerData.numberOfSteps);
yesterdaysNumbersLabel.text = [pedometerData.numberOfSteps stringValue];
[pedometerDictionary setValue:[pedometerData.numberOfSteps stringValue] forKey:#"2"];
}
});
}];
Using the above code I am able to get the data, log the data, and update the label on the screen, But I can't figure out how to set the data into an array or dictionary so I can do something else with it.
I understand why the arrays and dictionaries are always null... the blocks are running on a different thread and I am accessing them before the blocks have completed.
Can someone help me get through my head how to do something more with the data.
Update 1:
Right now I have this in .h
#property (strong, atomic) NSMutableDictionary *pedometerDictionary;
and I am synthesizing it in .m and I call this...
[self getNumbersForYesterday];
NSLog(#"Dictionary: %#", pedometerDictionary);
...which runs the above function and immediately tries to log the result. And like I said, I understand all the reasons it is NOT working. I just need to figure out how to change what i am doing to get it working.
Update 2:
This is in .h
#property (strong, atomic) NSMutableDictionary *pedometerDictionary;
and this is in .m
#synthesize pedometerDictionary;
- (id)init {
self = [super init];
if (self != nil) {
self.pedometerDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
and I am using it like this.
[self getNumbersForYesterday];
NSLog(#"Dictionary: %#", self.pedometerDictionary);
to call this.
- (void)getNumbersForYesterday {
[self.pedometer queryPedometerDataFromDate:yesterday
toDate:midnightOfToday
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"Pedometer is NOT available.");
}
else {
NSLog(#"Steps %#", pedometerData.numberOfSteps);
yesterdaysNumbersLabel.text = [pedometerData.numberOfSteps stringValue];
[self.pedometerDictionary setValue:[pedometerData.numberOfSteps stringValue] forKey:#"2"];
}
});
}];
}
If I just wanted to keep all the work in the block I would be fine. What I have come to understand is that since blocks are asynchronous, I am trying to NSLog my dictionary, and the block isn't finished running yet. So, my dictionary is still NULL.
Dollars to donuts, your pedometerDictionary was never created in the first place (or it was, but the declaration isn't in a useful spot).
I.e. where is your line of code that says pedometerDictionary = [[NSMutableDictionary alloc] init];? And where is pedometerDictionary declared? How did you try to NSLog() values from it?
Also, use setObject:forKey:.
It is also odd that it is named pedometerDictionary. That is evidence that it is either declared as a global (which it shouldn't be), a local variable of whatever method contains the above code (which won't work), or you are declaring and using an instance variable directly.
The issue you are having is not a block timing issue, your dictionary should never be nil at worst it would contain no values.
You need to create your dictionary before using it. The appropriate place would be init method for most objects. If you are creating your object in Interface Builder then the method should be awakeFromNib.
To do something with the dictionary you can use an NSTimer or call a method from queryPedometerDataFromDate block handler. The use of #synchronized() directive is an example of how to keep access to the dictionary from overlapping at the same time in a threaded environment. This is not the case in this particular example as you are dispatching on the main thread and NSTimer also runs on the main thread. But should you go threaded #synchronized() would keep you from overlapping access.
#interface HelloWorld : NSObject
#property (retain, atomic) NSMutableDictionary *pedometerDictionary;
#property (retain, nonatomic) NSTimer *timer;
#end
#implementation HelloWorld
#synthesize pedometerDictionary, timer;
...
- (id)init {
self = [super init];
if (self != nil) {
self.pedometerDictionary = [NSMutableDictionary dictionary];
self.timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(doSomethingInterestingWithDictionary:) userInfo:nil repeats:YES];
}
return self;
}
or
- (void)awakeFromNib {
self.pedometerDictionary = [NSMutableDictionary dictionary];
self.timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(doSomethingInterestingWithDictionary:) userInfo:nil repeats:YES];
}
...
- (void)getNumbersForYesterday {
[self.pedometer queryPedometerDataFromDate:yesterday
toDate:midnightOfToday
withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"Pedometer is NOT available.");
}
else {
NSLog(#"Steps %#", pedometerData.numberOfSteps);
yesterdaysNumbersLabel.text = [pedometerData.numberOfSteps stringValue];
#synchronized (self) {
[self.pedometerDictionary setValue:[pedometerData.numberOfSteps stringValue] forKey:#"2"];
}
[self doSomethingInterestingWithDictionary:nil];
}
});
}];
}
// Will be called when queryPedometerDataFromDate returns and from a timer every 5 seconds.
- (void)doSomethingInterestingWithDictionary:(NSTimer *)aTimer {
#synchronized (self) {
NSLog(#"My days dictionary: %#", self.pedometerDictionary);
}
}
So right now I'm working on sending the match data in a turn based game and I was using this post as a reference.
Good practices for Game Center matchData
I created a new class and it implements NSCoding. It currently only holds one variable for a NSString. This is the code for when I send the match data.
self.game.status = #"Test";
NSData *updatedMatchData = [NSKeyedArchiver archivedDataWithRootObject:self.game];
[self.currentMatch endTurnWithNextParticipants:[NSArray arrayWithObject:nextPerson]
turnTimeout:1000
matchData:updatedMatchData
completionHandler:^(NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
}
}];
NSLog(#"Successfully ended turn");
}
When I try retrieving the match data, I tried this.
[match loadMatchDataWithCompletionHandler:^(NSData *matchData, NSError *error) {
if (matchData)
{
RaceGame *updatedGame = [NSKeyedUnarchiver unarchiveObjectWithData:matchData];
NSLog(#"Match Data: %#", updatedGame.status); //prints null
callback(matchData);
}
}];
However, status is null. I've checked that match isn't null either. I also printed out the match and it said that matchData.length = 135, but I kept changing things around and it was still 135 so I'm not sure if that's helpful.
Any ideas on why status isn't changing?
--EDIT--
.m
#implementation RaceGame
#synthesize status;
#pragma mark - NSCoding protocol
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:status forKey:#"status"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.status = [aDecoder decodeObjectForKey:#"status"];
}
return self;
}
#end
.h
#interface RaceGame : NSObject <NSCoding> {
NSString *status;
}
/* Match Data */
#property (nonatomic, copy) NSString *status;
#end
Never mind, really stupid mistake by me. I was testing it on two devices and I only ran the updated version on one of the devices.
I would like to do the following:
In a class (shared instance) I will have a method that takes as parameters a data object (nsstring) and a delegate. This will create a new background thread and dispatch some calculations on that thread. The thing is that the method may be called hundreds of times with different data and possibly different delegates passed in . I would like the results to go to the correct delegate (I will need to keep the delegates in an array right? or can I just pass them to the background thread as they come and when that thread finishes it will send the result only to that delegate?).
One more thing... all this methods will use a very large data structure (an array with 10000 nsstring objects,they only need to read from it). How do I make sure this is not duplicated on each thread? And is only allocated when needed and deallocated when no thread uses it?
Here is the code I decided to use:
if (!self.dictPasswords) {
// read everything from text
NSString* fileContents = [NSString stringWithContentsOfFile:fileRoot
encoding:NSUTF8StringEncoding error:nil];
// separate by new line
self.dictPasswords = [fileContents componentsSeparatedByCharactersInSet:
[NSCharacterSet newlineCharacterSet]];
}
__weak id<PSPasswordDictionaryVerificationDelegate> wdelegate = delegate;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[wdelegate willBeginPasswordVerificationForPassword:password];
for (NSString *posiblePass in self.dictPasswords) {
if ([password isEqualToString:posiblePass]) {
dispatch_async(dispatch_get_main_queue(), ^{
[wdelegate password:password isInDictionary:YES];
});
return;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[wdelegate password:password isInDictionary:NO];
});
});
However... after this runs I get a permanent 24MB added to the used memory. I would like to detect when no threads are using the self.DIctPasswords array and deallocate it. It will be read from the file again later if somebody calls this method again...
Thanks for the help guys.
Just let the block capture the delegate.
No need to hold it otherwise
Class
#import <Foundation/Foundation.h>
#protocol ProcessorDelegate;
#interface Processor
- (void)process:(id)data forDelegate:(id<ProcessorDelegate>)delegate;
+ (Processor*)sharedInstance;
#end
#protocol ProcessorDelegate
- (void)processor:(Processor*)processor didProcess:(id)data withResult:(id)result;
#end
#implementation Processor
- (void)process:(id)data forDelegate:(id<ProcessorDelegate>)delegate {
__weak id<ProcessorDelegate> wdelegate = delegate; //capture weak to counter potential cycles
__weak id wself = self;
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSLog(#"WORK");
id result = data; //TODO
[wdelegate processor:wself didProcess:data withResult:result];
});
}
+ (Processor*)sharedInstance {
static Processor *p = nil;
if(!p) {
p = [[Processor alloc] init];
}
return p;
}
#end
DEMO
#interface Demo : NSObject <ProcessorDelegate>
- (void)doIt;
#end
#implementation Demo
- (void)doIt {
[Processor sharedInstance] process:#"TEST" forDelegate:self];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Demo *d1 = [[Demo alloc] init];
Demo *d2 = [[Demo alloc] init];
Demo *d3 = [[Demo alloc] init];
Demo *d4 = [[Demo alloc] init];
[d1 doIt];
[d2 doIt];
[d3 doIt];
[d4 doIt];
[[NSRunLoop currentRunLoop] run];
}
}
It seems more appropriate to encapsulate the calculations plus data and delegate in a class of its own. Then you can have an array of those objects in your singleton. You may want to consider using NSOperation here.
OMT: Simply pass this large array as a pointer (to each calculation object) and use regular strong properties (not copy) if you're using any properties at all, saving a reference to it using an ivar is fine too. One concern is that this data-structure must be read-only; otherwise (when you'd modify it in each thread), you'd need some data locking.
I have done it with blocks : a singleton that have all the functions you needs (like an API) and a delegate class
// singleton.h
typedef void (^request_handler_t)(NSData* data);
- (void) foo:(NSString *)str withBlock:(request_handler_t)callback;
// singleton.m
- (void) foo:(NSString *)str withBlock:(request_handler_t)callback;{
MyDelegate *delegate = [MyDelegate delegateWithBlock:callback];
[yourMethodThatNeedDelegate:delegate];
}
// MyDelegate.h
+ (MyDelegate*) delegateWithBlock:(api_request_handler_t)block;
- (void)delegateMethod1;
//Delegate.m
+ (MyDelegate*) requestWithBlock:(api_request_handler_t)block;{
//... alloc init
_callback = block;
}
- (void)delegateMethod1;{
// delegate finished the job
block(myResultingData);
}
// Usage :
[MySingleton singleton] foo:(NSString *)str withBlock:^(NSData *data){
//do something with the async data
}];