Search code examples
swiftobjective-cxcodeiphone-privateapimfmessagecomposeviewcontroller

How to access the method in the Private API framework and pass the value to it?


First - I know private frameworks/APIs won't get me to the AppStore, this is for private use/research only.

So as for the research purpose I chose MFMessageComposer I want to disable the editing of any inputs both of which are being passed from the code.

I tried to put my hands on this and I coded in the following manner . What I did is I took the path of the private framework and accessed a particular class called as CKSMSComposeController which have the above mentioned methods . I referred the class dump classes of the ChatKit.framework https://github.com/nst/iOS-Runtime-Headers/blob/master/PrivateFrameworks/ChatKit.framework/CKSMSComposeController.h

I am getting the logs of NSLog(@"Result %@", success ? @"YES" : @"NO"); as YES but still I am unable to disable the edit of recepients even after passing NO to the selector above

Can someone tell am I passing the parameter in a correct way ? Because -(void)setCanEditRecipients:(BOOL)arg1;` which is a method in the private framework accepts bool as parameter and I am passing NO in above code

This is just for internal research on private frameworks. Where I am doing wrong ?.Please tell


Solution

  • Class methods start with + and instance methods start with - in Objective-C.

    // Following is an instance method because it starts with `-`
    - (void)setCanEditRecipients:(bool)arg1;
    

    Above method will NOT work with following code.

    Class CKSMSComposeController = NSClassFromString(@"CKSMSComposeController");
    SEL sel = NSSelectorFromString(@"setCanEditRecipients:");
    
    // `CKSMSComposeController` is a class - NOT an instance
    if ([CKSMSComposeController respondsToSelector:sel]) {
        // will not enter if's body
    }
    

    On top of all this - you shouldn't create an instance of your own and do customizations on that. You should do the customizations on the instance that's presented by the system on screen.

    Here's how you can try that -

    - (void) showMessageComposeViewController {
        if ([MFMessageComposeViewController canSendText]) {
            MFMessageComposeViewController* messageController = [[MFMessageComposeViewController alloc] init];
            messageController.recipients = @[@"555-555-5555"];
            messageController.body = @"Example message";
            
            [self presentViewController:messageController animated:YES completion:^{
                
                // Allow enough time for the UI to be loaded fully
                dispatch_after(1, dispatch_get_main_queue(), ^{
                    // Since `MFMessageComposeViewController` is a `UINavigationController`, we can access it's first view controller like this
                    UIViewController* targetVC = messageController.viewControllers.firstObject;
                    
                    // Check if the instance is of correct class type
                    if ([targetVC isKindOfClass:NSClassFromString(@"CKSMSComposeController")]) {
                        
                        SEL sel1 = NSSelectorFromString(@"setCanEditRecipients:");
                        if ([targetVC respondsToSelector:sel1]) {
                            // put breakpoint here to check whether this line is executed
                            [targetVC performSelector:sel1 withObject:@NO];
                        }
                        
                        SEL sel2 = NSSelectorFromString(@"setTextEntryContentsVisible:");
                        if ([targetVC respondsToSelector:sel2]) {
                            // put breakpoint here to check whether this line is executed
                            [targetVC performSelector:sel2 withObject:@NO];
                        }
                    }
                });
            }];
        }
    }