#import "ViewController.h"
#interface ViewController ()
//Declare block as property
#property (nonatomic, strong) void (^dataBlock)(BOOL success);
#end
#implementation ViewController
- (void) myMethod1:(void (^)(BOOL success))response {
//Here data block holds the reference to response block
_dataBlock = response;
}
- (void) myMethod2 {
//Check for _dataBlock and invoke it.
if (_dataBlock) {
_dataBlock(YES);
}
}
- (IBAction) buttonClick {
//Call for myMethod1 and call back block is invoked in myMethod2
[self myMethod1:^(BOOL success) {
if (success) {
NSLog(#"Im Done");
}
}];
}
#end
Above sample is my code in Objective-C
Callback Block of "myMethod1"(response) is having reference/stored to "dataBlock" Property.
then invoke "dataBlock" from "myMethod2".
since "datablock" have reference to"myMethod1" block named "response", i'll be getting call back in "myMethod1", please look at the code snippet (similar to function to pointer).
same thing i want to implement in swift. I have tried implementing this in swift using closures, but not getting it.
No, there is not, unless it's on a Jailbroken device.
Apple does not let 3rd party apps alter the core behavior of the phone.
Now you could put the phone in a Faraday cage and put antennas on the inside and outside, and disconnect them when you wanted to block calls.
Actually you sort of can, but not really programmatically from iOS. If the BLE device implements the HID profile then you can simulate a double click on the lock button which would dismiss the call. I have done that, but it is a bit of a clunky solution.
Related
I know this seems like a simple question but if I #import <UIKit/UIKit.h> into a class file is this breaking the MVC rule?
The reason I ask is I set up a class that has a function to make an API call. Inside of that API function I want to use the dispatch_async method to get the data back to the main thread and want to call [tableView reload] inside of that dispatch_async method.
To do this I need to have access to the UITableView class so when I call my function I can pass in my tableView. Sorry still somewhat new to all of this.
Thanks!
First of all, MVC is a design pattern, and not a set of rules. It is a way of organizing code so that the view (and how to represent it) is totally decoupled from the model/application.
Secondly, you are correct in saying that importing the viewController file in the class that makes the API calls (let's call it API Class) is against the principles of MVC.
But you can make the view update in the viewController class itself! Instead of importing the ViewController class into the API class, you can do the absolute reverse and use the API class from the View Controller class. There are a lot of ways that classes can communicate with each other while conforming to the MVC pattern. Have a look at this article on the website objc.io which talks about the same.
Have a look at AFNetworking, an extremely popular networking library for applications written in Objective-C. It makes excellent use of objective-c blocks, and the same is recommended for your use case as well.
For example, your class for making the API call can look like this:
//APICall.h
#interface APICall : NSObject
-(void)makeAPICallWithHandler:(void(^)(NSError*, id data))handler;
#end
//APICall.m
#import "APICall.h"
#implementation APICall
-(void)makeAPICallWithHandler:(void(^)(NSError*, id data))handler
{
NSError *err;
id data;
//Make your API call and then pass the result in the handler
if (err)
{
handler(err, nil);
return;
}
handler(nil, data);
}
#end
The above method can be implemented by the view controller as follows:
//ViewController.m
#import "ViewController.h"
#import "APIClass.h"
#implementation ViewController
{
APIClass *api;
id displayData;
__weak IBOutlet UITableView *tableView;
}
-(void)viewDidLoad
{
[super viewDidLoad];
api = [APICall new];
[api makeAPICallWithHandler:^(NSError* error, id data) {
if (error)
{
//Show alert or something
return;
}
displayData = data;
[tableView reloadData];
}];
}
#end
The point is that using blocks, you can make the data from the API call available to the viewController which can then update the tableView and maintain it's data source. This conforms to MVC as you will have totally decoupled the view update from the actual API call.
As Apple encourages the usage of blocks, and i wanted to do a series of animations, with sound output in between them which is basicly like a todolist, i wanted to implement this using blocks.
unfortunatly AVAudiosplayer doesnt appear to support onCompletion blocks, in the manner UIAnimation does.
So i thought it would be cool to add that support to the AVAudioplayer.
so what ive dont is this
header
#import <AVFoundation/AVFoundation.h>
#interface AVAudioPlayer (AVAudioPlayer_blockSupport)
typedef void(^AVPlaybackCompleteBlock)(void);
#property (nonatomic, copy) AVPlaybackCompleteBlock block;
-(id)initWithContentsOfURL:(NSURL*)pathURL error:(NSError**)error onCompletion:(AVPlaybackCompleteBlock) block;
-(void)setBlock:(AVPlaybackCompleteBlock)block;
-(AVPlaybackCompleteBlock)block;
-(void) executeBlock;
#end
and the m file
#import "AVAudioPlayer+blocks.h"
#implementation AVAudioPlayer (AVAudioPlayer_blockSupport)
-(id)initWithContentsOfURL:(NSURL *)pathURL error:(NSError **)error onCompletion:(AVPlaybackCompleteBlock )block {
self = [[AVAudioPlayer alloc] initWithContentsOfURL:pathURL error:error];
self.block = block;
return self;
}
-(void)setBlock:(AVPlaybackCompleteBlock)block {
self.block = block;
}
-(AVPlaybackCompleteBlock)block {
return self.block;
}
-(void) executeBlock {
if (self.block != NULL) {
self.block();
}
}
#end
after doing this, i thought i should be able to create a new audioplayer like this:
player = [[AVAudioPlayer alloc] initWithContentsOfURL:pathURL error:&error onCompletion:block];
this appears to be working.
now in the delegate will try to execute the block attached.
if (localPlayer.block) {
[localPlayer executeBlock];
}
unfortunately when i try to run the code, it appears to be looping infinitely. I wanted to use synthesize instead, but thats not for category use...
If i dont implement that Method im stuck with '-[AVAudioPlayer setBlock:]: unrecognized selector sent to instance which makes sense, since there is no method with that name.
i found this Block references as instance vars in Objective-C so i thought i should be able to attach the additional property(my Block) to the AudioPlayer.
I figured it out, i needed to use
objc_setAssociatedObject(self, &defaultHashKey, blocked, OBJC_ASSOCIATION_COPY_NONATOMIC);
to store and access the property. maybe thats what jere meant with, i have tot ake care of the memory management myself.
-(void)setBlock:(AVPlaybackCompleteBlock)blocked {
objc_setAssociatedObject(self, &defaultHashKey, blocked, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(AVPlaybackCompleteBlock)block {
return objc_getAssociatedObject(self, &defaultHashKey) ;
}
I want to define the method which will contain the block as an argument but block should be run on the completion of the method.
For Example:
[picker dismissViewControllerAnimated:YES completion:^{
imageThumb = pickedImage;
imageViewThumb.image = imageThumb;
}];
Please have a look what i did yet.
I declared the method in .h file-
-(void)resizeImageForSmoothness: (int) imageSmoothness completion: (void (^)(void))completion;
I implemented it in .m file-
-(void)resizeImageForSmoothness:(int)imageSmoothness completion: (void (^)(void))completion
{
// Here i performed my image resizing activity
}
How can my code will know that method has been completed and then run the completion block?
How can we declare and define such method?
How to store the block depends on how you do your stuff. If it's a synchronous operation (that is, the method blocks until whole operation is complete) you simply call it like a function:
- (void)fooWithHandler:(void(^)())handler
{
// Do things.
handler();
}
If the operation is asynchronous, you might want to store the block in a variable or even a dictionary. In this case you need to copy the block. You can either do this via the low-level Block_copy and Block_release C functions, but you can also treat a block like an Objective-C object! (Xcode doesn't provide autocompletion for this, for some reason.)
#interface MyClass {
void (^myHandler)();
}
- (void)fooWithHandler:(void(^)())handler
#end
#implementation MyClass
- (void)fooWithHandler:(void(^)())handler
{
myHandler = [handler copy];
// Do things.
// Then, when you're done (this is probably in another method):
if (myHandler) {
myHandler();
myHandler = nil;
}
}
#end
You can do something like that and use the return type et parameter you might need :
- (void)doStuffAndExecute:(void (^)(void))handler
{
// do stuff
handler();
}
I have a question about passing data in iOS/Objective-C. I just started creating an app that connects and fetches data from a website, and now I'm having a problem.
My default and root view controller is called ViewController. It has the basic login UI views: 2 textfields and a button.
When the button has been clicked/touched, the ViewController calls a method from another class called LoginService. Now LoginService handles the connection to the website. I have no problem connecting to the website and fetching data from it, but I have a problem returning the fetched data, now processed as an NSDictionary, to the ViewController.
The first thing I tried was to create a setter method in the ViewController that sets the instance variable userProfile to the NSDictionary passed into it. It failed, however. I tried using it in the NSURLConnectionDataDelegate method connectionDidFinishLoading from the LoginService.
This might be a silly question, but I have no idea how can I pass the fetched NSDictionary from LoginService to the ViewController after the button is clicked. Do I need blocks, queue, or something else? I mean, for example, I need to set a label below my login button to the name of the user who logged in. How can I perform this?
Hope someone can help me. I'd greatly appreciate it.
As danh has explained blocks pattern for doing this, I will try to explain the delegating pattern. The steps for making this work:
In LoginService.h
Create a protocol definition in your LoginService like this:
#protocol LoginServiceDelegate
-(void)applicationLoggedIn:(NSMutableDictionary*) responseData;
#end
Now add a member pointer holding this delegate and add a property for this
#interface LoginService {
id<LoginServiceDelegate>delegate;
}
#property (nonatomic, assign) id <LoginServiceDelegate> delegate;
In LoginService.m
Once you got the response for login in connectionDidFinishLoading, just invoke the delegate method like below:
if ([delegate respondsToSelector:#selector(applicationLoggedIn:)]) {
[delegate applicationLoggedIn:responseDict];
}
In LoginViewController.h
Now to use this in your LoginViewController, you need to implement this protocol
#import "LoginService.h"
#interface LoginViewController<LoginServiceDelegate>
In LoginViewController.m
Assign the delegate of LoginService to LoginViewController
LoginService* loginService = [[LoginService alloc]init];
loginService.delegate = self;
Implement the protocol method as:
-(void)applicationLoggedIn:(NSDictionary*)response{
}
Hope this helps.
Two patterns to consider: delegate and block. Block is quicker to code, and I usually prefer it to delegate for network ops. To use a block, write the login service this way:
// LoginService.h
- (void)login:(NSString *)username completion:(void (^)(NSDictionary *, NSError *))completion;
It sounds like you're using NSURLConnection delegate pattern here, so I will assume that. Please realize that NSURLConnection also provides a nice one-shot block method to do the request.
// LoginService.m
#property (copy, nonatomic) void (^completion)(NSDictionary *, NSError *);
- (void)login:(NSString *)username completion:(void (^)(NSDictionary *, NSError *))completion {
// copy the block when the request begins
self.completion = completion;
// start your request, as you have it now
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSDictionary *dictionary = // parse the data you collected into a dictionary
// invoke the block with the result
self.completion(dictionary, nil);
self.completion = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
self.completion(nil, error);
self.completion = nil;
}
It's polite to dispose of the block (set it to nil) after you invoke it, so it doesn't retain any part of the calling context.
Basically you need ViewController to have a public method which LoginService can call when it's done its job, and NSDictionary will be a parameter to this method. LoginService will need a reference back to ViewController in order to invoke this method, so define a public property on LoginService which will hold a ViewController reference - and set this right after instantiating LoginService.
Of course, if you want LoginService to be more reusable, and not tied to ViewController specifically, delegates are the way to go. LoginService would define the LoginServiceDelegate protocol with the method to be called on completion. ViewController would then implement the LoginServiceDelegate protocol. The public property on LoginService becomes a LoginServiceDelegate reference, and so LoginService no longer needs to import ViewController. This way, ViewController is dependent on LoginService, but LoginService is not dependent on ViewController.
I want to create a completion handler for a certain class, instead of firing off the class's main code and waiting for a delegate callback. I've read through the Apple documentation and they don't seem to give a very good example of how to directly implement something like this.
You need to treat the completion block just like a variable. The method will accept a block as part of it's parameters, then store it for later.
- (void)myMethodWithCompletionHandler:(void (^)(id, NSError*))handler;
You can typedef that block type for easier reading:
typedef void (^CompletionBlock)(id, NSError*);
And then store your block as an instance variable:
In your #interface: CompletionBlock _block;
In the myMethod.. _block = [handler copy]
Then when you want the completion block to execute you just call it like a regular block:
_block(myData, error);
If it was for an asynchronous method you could do it like this
- (void)asynchronousTaskWithCompletion:(void (^)(void))completion;
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Some long running task you want on another thread
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion();
}
});
});
}
this would be invoked with
[self asynchronousTaskWithCompletion:^{
NSLog(#"It finished");
}];
Something to note is the guard to make sure that completion is pointing to something otherwise we will crash if we try to execute it.
Another way I often use blocks for completion handlers is when a viewController has finished and want's to be popped from a navigation stack.
#interface MyViewController : UIViewController
#property (nonatomic, copy) void (^onCompletion)(void);
#end
#implementation MyViewController
- (IBAction)doneTapped;
{
if (self.onCompletion) {
self.onCompletion();
}
}
#end
You would set the completion block when pushing this view onto the stack
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
MyViewController *myViewController = segue.destinationViewController;
myViewController.onCompletion = ^{
[self.navigationController popViewControllerAnimated:YES];
};
}
Heres an example for a method that takes a String and a completion handler as variables. The completion handler can also receive a String.
Swift 2.2 Syntax
Defintion:
func doSomething(input: String, completion: (result: String) -> Void {
print(input)
completion(result: "we are done!")
}
Calling the function:
doSomething("cool put string!") { (result) in
print(result)
}
Chris C's answer is correct (and was very helpful to me) with one caveat:
Placing the declaration CompletionBlock _block; in #interface is not thread safe.
Put CompletionBlock _block = [handler copy]; in myMethod… instead if there is any possibility that myMethod… will be called from multiple threads (or dispatch queues).
Thanks #Chris C.