I will explain the need based on below example
This is the method that need to be called after an async operation
-(void) myCallbackMethodOne: (NSString *)response
{
//method to be called
}
-(void) myCallbackMethodTwo: (NSString *)response
{
//method to be called
}
-(void) getDataFromServerWithCallback: (NSString *)requestString _Callback(CallbackMethod *) methodName
{
//logic to send request and
//to set callback method something similar to
[setCallbackMethod methodName];
}
-(void) onDataRecievedFromServerWithResponse: (NSString *) response //this method gets called as part of framework
{
[callTheCallbackMethod: response];
}
A place to call the method to demonstrate the requirement
-int main()
{
[getDataFromFromServerWithCallback: #"getTopNews" _Callback:myCallbackMethodOne]; //this is the requirement; I should be able to pass myCallbackMethod as argument
[getDataFromFromServerWithCallback: #"getBusinessNews" _Callback:myCallbackMethodTwo]; //this is the requirement; I should be able to pass myCallbackMethod as argument
}
There are two well established patterns for this type of functionality:
1) Delegate:
#protocol ResponseDelegate
- (void)handleResponse:(NSString *)response;
#end
#interface CommsClass : NSObject
#property (weak) id<ResponseDelegate> delegate;
- (void)sendRequest:(NSString *)request;
#end
#interface CallingClass : NSObject <ResponseDelegate>
{
CommsClass _commsClass;
}
- (void)callingCode;
#end
#interface CallingCode
- (void)callingCode
{
_commsClass = [CommsClass new];
_commsClass.delegate = self;
[_commsClass sendRequest:#"Blah"];
}
- (void)handleResponse:(NSString *)response
{
NSLog(#"Whoot: %#", response);
}
#end
2) Blocks.
typedef (^HandleResponseBlock)(NSString *response);
#interface CommsClass : NSObject
- (void)sendRequest:(NSString *)request
withCompletionBlock:(HandleResponseBlock)block;
#end
For objective C, I believe you have to pass the callback using block. But for Swift, since methods are also first class citizens, we can do something like this:
func buttonDidTapped(sender: AnyObject!) {
doSomethingWithCallback(callbackFunc: myCallback)
}
func doSomethingWithCallback(callbackFunc: (NSDictionary)->()) {
//do something
callbackFunc(["param": "pass any param by dynamic dictionary"])
}
func myCallback(infoDict: NSDictionary) {
//callback implementation
}
We can define the callback and as any functions and pass it like any parameters.
For more information on using objective-c and swift in the same project, please refer to Apple document:
https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html
Hope that it helps.
I got my requirement working by using selectors
-(void) myCallbackMethodOne: (NSString *)response
{
//method to be called
}
-(void) myCallbackMethodTwo: (NSString *)response
{
//method to be called
}
-(void) getDataFromServerWithCallback: (NSString *)requestString _Callback:(SEL) methodName _Caller(id):callingClass
{
//write the logic here to store methodname and caller to reference variables
//so that it will be accessible in onDataRecievedFromServerWithResponse
//and to send the request
}
-(void) onDataRecievedFromServerWithResponse: (NSString *) response //this method gets called as part of framework
{
[callingClass performSelector:methodName withObject:response];
}
-int main()
{
SEL methodOneSelctor =#selector(myCallbackMethodOne:);
[getDataFromFromServerWithCallback: #"getTopNews" _Callback:methodOneSelctor _MethodCaller:self]; //I should be able to pass myCallbackMethod as argument
SEL methodTwoSelctor =#selector(myCallbackMethodTwo:);
[getDataFromFromServerWithCallback: #"getBusinessNews" _Callback:methodTwoSelctor _MethodCaller:self]; //I should be able to pass myCallbackMethod as argument
}
Related
I have the following two methods in an custom objective c framework
- (void) callWithProfile:(UserProfile *) user number:(NSString *) number name:(NSString *)name instructions:(NSString *)instructions
{
//
}
// Make call connection
- (void) startConnecting
{
//
}
and I can call startConnecting with
let socket : SocketSingleton = SocketSingleton()
socket.startConnecting()
but it won't let me call callWithProfile. Both methods are declared in the header file and there are no errors in the framework. How can I call the other method from my swift project?
This sounds ridiculous but its not working. Can someone see what I am missing?
I have a ViewController in which I defined a function called ProcessData. I am trying to call ProcessData from the viewDidLoad function but it does not see it. I declared it in the .h file and then defined it in the .m file. Here is the code.
ViewController.h
#interface ViewController : UIViewController
- (void) ProcessData: (NSString *)response;
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[ProcessData nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) ProcessData: (NSString *)response
{
}
#end
To call a class / static function you have to write
[ViewController ProcessData: nil];
you have to call the method ProcessData on the class ViewController.
But what you have here is not a class / static method since it is - (void) instead of + (void). Your mistake is basically using the wrong syntax for calling any method. You call methods on objects like
[self ProcessData: nil];
instead of
[ProcessData: nil];
Final Note: method names should start with a lower character! The method should be called processData!
In this case, you haven't declared a class method, you've declared an instance method, so you'd call it by doing:
[self ProcessResponse: nil];
If you want to declare a class function, you need to use a + at the start if the declaration instead of a -:
+ (void) ProcessResponse: (NSString *)response;
You'd call this like this:
[ViewController ProcessResponse: nil];
As a side note, you should actually follow common convention by formatting like this:
+ (void)processResponse:(NSString *)response;
Note pascal case (no capital at the start of the method name), no space between return type and name, and no space between method name and the parameter type.
It is not a class method. It's a instance method. You have to call it like this:
[self ProcessResponse:nil];
Class method is defined with "plus" symbol (+), not a hyphen (-).
And method name should start with lowercase.
I'm new to Objective-C and I'm trying to determine if the NSString being passed to my method is the same as the NSString previously passed to the same method.
Is there a simple way to do this?
If you're looking to do this per instance of your class (and not globally):
#interface MyClass : NSObject
- (void)myMethod:(NSString *)value;
#end
#interface MyClass ()
#property (copy, nonatomic) NSString *value;
#end
#implementation MyClass
- (void)myMethod:(NSString *)value
{
if ([self.value isEqualToString:value])
{
// Values are the same!
}
else
{
self.value = value;
}
}
#end
Store the string as an instance variable of the class, each time the method is called, compare the instances and replace with the new parameter.
Just to elaborate on what #Wain said:
Add Instance Variable:
#interface ViewController ()
{
NSString * lastString;
}
Then in your method:
- (void) methodWithString:(NSString *)string {
if ([string isEqualToString:lastString]) {
NSLog(#"Same String");
}
else {
NSLog(#"New String");
lastString = string;
}
}
A variation on the theme most answers are following: If you wish to do this per instance of your class then you can use an instance variable. However as this is something which is really specific to your method, you don't want to declare this variable in any interface and recent compilers help you with this by enabling instance variable declaration in the implementation. For example:
#implementation MyClass
{
NSString *methodWithString_lastArgument_; // make it use clear
}
- (void) methodWithString:(NSString *)string
{
if ([string isEqualToString:methodWithString_lastArgument_])
{
// same argument as last time
...
}
else
{
// different argument
isEqualToString:methodWithString_lastArgument_ = string.copy; // save for next time
...
}
}
(The above assumes ARC.)
The string.copy is shorthand for [string copy] and is there to handle mutable strings - if the method is passed an NSMutableString then this will copy its value as an (immutable) NSString. This protects the method from the mutable string changing value between calls.
If you want to do this on a global basis, rather than per instance, you can declare a static variable within your method and thus completely hide it from outside the method:
- (void) methodWithString:(NSString *)string
{
static NSString *lastArgument = nil; // declare and init private variable
if ([string isEqualToString:lastArgument])
{
// same argument as last time
...
}
else
{
// different argument
isEqualToString:lastArgument = string.copy; // save for next time
...
}
}
HTH
I have three UIViewController that call a method in another Class, when there is need.
This fourth Class subclass of NSObject, which I will call CheckController, it makes a connection to a database on a server, and to do this takes a few seconds.
I have two questions to ask:
-FIRST: now to get the array back to me from the server, use a method that is almost embarrassing for me to share (bad code for me),
so i need to know how to call this CheckController and return a value in the ViewController from which the call originated.
-SECOND: how do I know in CheckController, from which UIViewController, the method was invoked?
this is code of CheckController:
-(void)connectionWithString:(NSString *)string {
//connection with server - work well
}
...
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
...
[self returnArray:myObject];
}
-(void)returnArray:(NSMutableArray *)arrayReturn {
//in this method i set the BOOL done to YES, but i believe that is it possible to
//send directly this arrayReturn to ViewController that invoked this method
done = YES;
NSLog(#"arrayReturn = %#", arrayReturn);
}
thanks in advance for the help, and tell me if something is not clear
As you already mentioned your Class is a subclass of NSObject which is good. But you should provide a protocol which calls the delegate if your CheckController receives some data. So in your CheckController.h
#protocol CheckControllerDelegate <NSObject>
#required
- (void)receivedArray:(NSArray*)data
#end
#property (nonatomic, weak) id <CheckControllerDelegate> delegate;
In your ViewController:
[self.CheckController setDelegate:self];
CheckController.m:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[_delegate receivedArray:myObject];
}
For best practice your protocol should also have a method which would be called if an error occurs.
I have an helper class which has a function that makes an api call and get some json data and formats and returns an array. My TableViewController is trying to access that returned array. Yes, as you expected my tableviewcontroller viewDidLoad method is not able to access the array object returned by my helper class.
#interface MyHelperClass : NSObject
#property (nonatomic,retain)NSArray *myArray;
#end
#implementation MyHelperClass
#synthesize myArray;
- (NSArray *) returnArray{
// make api calls and return array
return myArray;
}
#end
#implementation MyTableViewController
{
- (void)viewDidLoad
{
[super viewDidLoad];
MyHelperClass *myhelper = [[MyHelperClass alloc]initWithPath:getSpacePath];
allTopics = (NSArray *)[myhelper returnArray];
NSLog(#"Load my Array%#",allTopics);
}
}
My question is, do I need to implement a delegate to pass the data around or is there any other way to pass around the data to my view controller?
P.S : I do not want to use global variable
Did this code give you any warning ?
You are trying to return an NSArray * from void returning method.
Modify it to
- (NSArray *) returnArray{ // YOU CAN RETURN id AS WELL, AS YOU ARE TYPE CASTING THE RESULT AT CALLING TIME
// make api calls and return array
NSLog (#"myArray :: %#", [myArray description]); // Post the output back here
return myArray;
}
Let me know if the problem persists.
EDIT
Set breakpoints at
allTopics = (NSArray *)[myhelper returnArray]; // AT - (void)viewDidLoad
and
return myArray; // AT HelperClass method
If first one it getting fired first, then You have to implement as #A-Live suggested in the comment.
Sorry for posting the answer so late. I figured out what the problem is. As #A-Live mentioned, the Rest API calls using AFNetworking is using async calls and hence it's not returning the array to the main thread within it's execution time. In my case,
-(void)viewDidLoad {
NSLog(#"I get called first");
MyHelper *helper = [[MyHelper alloc]init];
// returns array. However, [helper getData] is an async call under the hood. Hence myArray is nil
myArray = [helper getData];
}
To solve this problem, I took advantage of NSNotification.
#implementation MyHelper{
-(NSArray *)getData(){
[[NSNotificationCenter defaultCenter] postNotificationName:#"some.name.notification" object:JSON];
}
}
-(void)viewDidLoad(){
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadData:) name:#"some.name.notification" object:nil];
}
-(void)loadData:(NSNotification *)notif {
// You can access the JSON object passed by the helper in here
NSArray *myArray = [notif object];
// do whatever you want with the array.
}
I hope I am detailed enough. I hope this helps someone and saves a lot of headache.