Search code examples
objective-cappdelegateurl-scheme

Where to from AppDelegate? how to go back to previous view and access instance


I have an instance in a view called "PayView" and at the meantime I am doing openURL to open a separate app and to pass some data to it. This second app process the information I send and gives the response back.

At Appdelegate.m I have this handleOpenUrl which receives the response sent by the second app. Once I receive the response back I would like to go back to my "PayView" and use the response received from the second app along with already existing values I have in the instance.

My problem is as soon as the response from the second app reaches the appdelegate and reached the section "return yes", my main app goes back to this "PayView" and does nothing.

So how do I use the response object I received from appdelegate at my PayView along with already existing instance values?

I thought about using a global variable to store the "payView" instance/object and initiate a new instance from appdelegate for "Payview" and use the global along with the response json from appdelegate. However, I found many forums advising against using global.

So, ignoring the global and crating a new instance for "payview" causes loss of all previously stored data.

I am not an experienced iOS programmer and just work momentarily on someone else code.So, hope I explained my issue/question.

It would be great if I could get some valuable inputs :)

My appdelegate.m look like this,

-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
    if (!url) {
        return NO;
    }

    // Unencode the URL's query string
    NSString *queryString = [[url query] stringByRemovingPercentEncoding];

    // Extract the JSON string from the query string
    queryString = [queryString stringByReplacingOccurrencesOfString:@"response=" withString:@""];

    // Convert the JSON string to an NSData object for serialization
    NSData *queryData = [queryString dataUsingEncoding:NSUTF8StringEncoding];

    // Serialize the JSON data into a dictionary
    NSDictionary* jsonObject = [NSJSONSerialization JSONObjectWithData:queryData options:0 error:nil];

    NSString *response = [jsonObject objectForKey:@"Result"]; //Get the response

    UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:@"Result" message:response delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert1 show];

    //PayView *pdv = [[PayViewController alloc] init];

    //[pdv UseTransactionResult:jsonObject] ;


    return YES;
}

and this is how I am calling open url from PayView

[[UIApplication sharedApplication]openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mysecondapp://v2/data/?request=%@",requestEncodedString]] options:@{} completionHandler:^(BOOL success) {
            if (success)
            {
                NSLog(@"Opened url");
            }
        }];

Edit 1:

Hi @ekscrypto, thanks for your valuable input. It is really helpful. I just have one problem with this. It is working just fine when I do the following in my PayView

Receiver:

[[NSNotificationCenter defaultCenter] addObserverForName:@"ActionIsComplete" object:nil queue:nil usingBlock:^(NSNotification *note){
        
        //Completed Action
        NSString *response = [note.userInfo objectForKey:@"Result"]; //Get the response
        NSLog(response);
        [self dismissViewControllerAnimated:YES completion:nil];
    
    }];

  However, when I try to do the same in the following  method I get error "unrecognized selector sent to instance"

Receiver:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ReceiveActionIsComplete:) name:@"ActionIsComplete" object:nil];

-(void)ReceiveActionIsComplete:(NSNotification *) notification
{

    NSDictionary *jsonObject = (NSDictionary *)notification.userinfo;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"ActionIsComplete" object:nil];

    Status = [jsonObject objectForKey:@"Status"];
    Result = [jsonObject objectForKey:@"Result"];

    NSLog([NSString stringWithFormat:@"%@%@",@"Status is: ", Status]);
    NSLog([NSString stringWithFormat:@"%@%@",@"Result is: ", Result]);
}

in both cases my sender at Appdelegate looks like this.

Sender:

[[NSNotificationCenter defaultCenter] postNotificationName:@"ActionIsComplete" object:nil userInfo:jsonObject];

FYI: I tried sending the object in object instead of userInfo as well.

So what I am doing wrong? could you please help me.

Conclusion:

Sender: (AppDelegate.m)

[[NSNotificationCenter defaultCenter] postNotificationName:@"ActionIsComplete" object:nil userInfo:jsonObject];

Receiver: (PayView.m)

under - (void)viewDidLoad

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ReceiveActionIsComplete:) name:@"ActionIsComplete" object:nil];

and the function to receive the results

-(void)ReceiveActionIsComplete:(NSNotification *) notification
{

    NSDictionary *jsonObject = (NSDictionary *)notification.userInfo;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"ActionIsComplete" object:nil];

    Status = [jsonObject objectForKey:@"Status"];
    Result = [jsonObject objectForKey:@"Result"];

    NSLog([NSString stringWithFormat:@"%@%@",@"Status is: ", Status]);
    NSLog([NSString stringWithFormat:@"%@%@",@"Result is: ", Result]);
}

Solution

  • You could register a NotificationCenter observer in your PayView, and post a notification from your AppDelegate when the response is received. Via the notification object you can forward any information you need.

    Let's assume that you define a struct with the information you want to pass along:

    struct PaymentConfirmation {
       let amount: Float
       let confirmationNumber: String
    }
    

    In your PayView:

    class PayView: UIView {
    ...
       static let paymentProcessedNotification = NSNotification.Name("PayView.paymentProcessed")
       let paymentProcessedObserver: NSObjectProtocol?
    
       override func viewDidLoad() {
          super.viewDidLoad()
          paymentProcessedObserver = NotificationCenter.default.addObserver(
              forName: PayView.paymentProcessedNotification,
              object: nil,
              queue: .main) { [unowned self] (notification) in
                  guard let confirmation = notification.object as PaymentConfirmation else { return }
                  self.paymentProcessed(confirmation)
          }
       }
    
       deinit {
           NotificationCenter.default.removeObserver(paymentProcessedObserver)
       }
    
       func paymentProcessed(_ confirmation: PaymentConfirmation) {
          // do stuff
       }
    
    

    Then in your AppDelegate:

        func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
            // do stuff required with the callback URL
            let confirmation = PaymentConfirmation(
                amount: 3.0, 
                confirmationNumber: "Pay830da08.8380zSomething")
            NotificationCenter.default.post(
                name: PayView.paymentProcessedNotification, 
                object: confirmation)
        }
    

    For more information, check out https://medium.com/ios-os-x-development/broadcasting-with-nsnotification-center-8bc0ccd2f5c3