Inside MFMessageComposeViewController , MFMessageComposeViewController used as the actual user interface from the private ChatKit.framework , which uses a whole group of other classes ( CKSMSComposeQueuingRemoteViewControllerProxy , CKSMSComposeRemoteViewController , CKSMSComposeViewServiceController ), including the XPCProxy<CKSMSCompose> interface.
UPDATE 1
After reading the quellish comment, I found this http://oleb.net/blog/2012/10/remote-view-controllers-in-ios-6/ It looks like MFMessageComposeViewController and similar classes start another process for their own purposes. These processes are XPC services that implement certain protocols through which your application communicates with them. These processes are signed with all necessary rights. Probably /Applications/MessagesViewService.app/MessagesViewService is what actually sends SMS messages. This binary is signed with com.apple.messages.composeclient , which is required to send messages with this code, which I found in ChatKit.framework. I think there is a way to contact this XPC service manually to send SMS, but it will be difficult. Not something you could do by cheating with a dump class and similar simple tools that don't really give you much information.
UPDATE 2
I managed to figure it out. I selected all helper classes, all interface elements that do not actually interact with the XPC service. What is left is enough to display the SMS view controller and send some methods to the XPC service without any action.
When we show the composition of SMS, the iOS user interface really starts the process /Applications/MessagesViewService.app/MessagesViewService . This is an XPC service. You can try to kill it while your application displays it - you will get a black screen, which means that it is correct.
CKSMSComposeRemoteViewController is a view controller that displays the SMS user interface. This is a subclass of the _UIRemoteViewController class. Basically, it manages the connection between our application and the actual user interface that runs inside the XPC service in another process. This is how I get an instance inside my method -(void)viewDidLoad
_UIAsyncInvocation* cancelationInvocation = [CKSMSComposeRemoveViewController requestViewController:@"CKSMSComposeViewServiceController" fromServiceWithBundleIdentifier:@"com.apple.mobilesms.compose" connectionHandler: ^(CKSMSComposeRemoteViewController* obj, NSError* error){ smsViewController = obj; [smsViewController setDelegate:self]; smsViewControllerProxy = [smsViewController serviceViewControllerProxy]; }];
smsViewController is an instance of a remote view controller. smsViewControllerProxy is an XPCProxy<CKSMSCompose> instance that implements a call to the CKSMSComposeViewServiceProtocol protocol - the method will be redirected to the XPC service. XPCProxy does not implement these methods. It implements the forwardInvocation: method to forward calls to the XPC connection.
_UIAsyncInvocation , looking at the name ivar cancelationInvocation , is used to cancel XPC messages. It is called only in the CKSMSComposeController viewServiceDidTerminateWithError: method.
This is how I show the controller:
[self presentViewController:smsViewController animated:YES completion:NULL];
You probably noticed [smsViewController setDelegate:self] . The delegate must implement the CKSMSComposeRemoteViewControllerDelegate protocol or application crashes with an exception (unrecognized selector). Here is what I implemented:
-(void)smsComposeControllerAppeared { } -(void)smsComposeControllerCancelled { } -(void)smsComposeControllerDataInserted { }
This is enough to go.
And here is how we can send a message to the remote view controller:
[smsViewControllerProxy insertTextPart:@"Some text"];
Paste the text into the SMS text box and call the smsComposeControllerDataInserted delegate method.
Given all this, I no longer think that we can send SMS messages without an interface. The actual user interface works in a different process, we do not control it. We can set some fields, but it is. I was hoping there was a way, but itβs true, not surprisingly, that we cannot. Remote views were introduced in iOS 6 to solve this security problem - on iOS 5 there was a way to send SMS without user permission, and some AppStore applications did this. So Apple came in handy.
UPDATE 3
I was able to send XPC messages sent while interacting with the SMS user interface. A few words about the format of magazines. The first line - the message was intercepted. This is either the name of the function or the Incoming event , which means its incoming message from the service. Connection name is just the name of the XPC connection. Message - the contents of the XPC message dictionary. Message data - some XPC messages contain binary data with the keys "d" β "r". This is a serialized list of binary properties that cannot be deserialized using NSPropertyListSerialization - in some new format. Instead, you need to use NSXPCDecoder -(id)_initWithRootXPCObject:(xpc_object_t) from Foundation.framework . And now the dumps:
Presented SMS user interface http://pastebin.com/NVEpujSh
SMS user interface after clicking the "Send" button http://pastebin.com/BYXd2djF
There are messages sent and received during editing SMS fields, but they correspond only to user interface events. For example, who will be the first responder that actually does not say anything about what is happening in the SMS user interface.