iMessage in fresh versions of iOS has learned to work with third-party extensions. For example, you can now add cats to messages, or even transfer money to someone without additional details. This is a lazy dream - to send money without leaving the messenger, so the development of Yandex.Money sat down for implementation.
When developing fashionable magic, quests were not without cost, because iMessage tells almost nothing about the recipient of the message. There is neither a wallet number, nor a full name, nor at least a static ID. But we came up with a way to find out about the addressee everything you need to send money.
... because there is a native Yandex.Money application, where you only need to specify the recipient’s wallet number. This can be answered with a counter question: why not implement a simplified mechanism that will not require the user to know the wallet number and switch between applications in the money transfer process?
Or you can answer even shorter - because people want it, and we can.
... and no questions in the spirit of "what is your wallet number?", "Where to transfer money?".
And here the most interesting begins. Apple is responsible for protecting personal data, so the iMessage extension knows almost nothing about your partner. Moreover, it will not even get your own phone number from iOS, which does not allow for a head-on transfer with the request for a purse number from the Yandex.Money payment service for some unique features of the iMessage subscriber.
If you don’t get any specifics from iMessage, you will have to work around it. For example, you can exchange the necessary information in the form of service background messages. To make it clearer, let’s analyze all this in the code using the example with the main application Yandex.Money and our extension for iMessage.
Of course, everything described below can be used in other scenarios or applications, not necessarily limited to translations.
First of all, you need to establish data exchange between iMessage and the main application, for which Yandex.Money was included in the App Groups:
// user defaults: let appGroupUserDefaults = NSUserDefaults(suiteName: "com.bundle.group") // — let appGroupDirectoryPath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "com.bundle.group")
After that, the iMessage extension opens access to the NSUserDefaults user parameters list and the application's local database. Let's add another Keychain Sharing to access the Keychain storage of mobile Yandex.Money — the payment token is stored there:
For translations, you will probably need to show individual screens of the application with pre-filled information, therefore, the processing of your own URL schemes that were previously implemented in Yandex.Money turned out to be very useful. This allowed you to open the desired program window directly from iMessage by simply navigating to a specific URL. This completes the preparations, and you can proceed to the message setup.
To create interactive messages, we use the MSMessage class, in which we define the appearance of a new element through the layout property:
let layout = MSMessageTemplateLayout() layout.image = UIImage(named: "moneyTransfer") layout.caption = "$\(conversation.localParticipantIdentifier.uuidString) " message.layout = layout
This was the translation interface in the message.
For more customization in MSMessage, you can set your own image, text fields and URL.
In any iMessage conversation, the interlocutors are represented by abstract UUIDs of the form "154D1B67-FF3B-40E2-AB53-49DD127BB1FA". By UUID, you can understand the number of interlocutors in a chat or use it to get readable usernames in text fields.
For example, we can add a text description to a new message in which the system replaces the UUID with the sender's name:
let newMessage = MSMessage(session: activeConversation.selectedMessage.session) newMessage.summaryText = "$\(conversation.localParticipantIdentifier.uuidString) \(amount) ₽" … conversation.insert(newMessage)
You might think that the UUID is unique throughout the entire iMessage network and that the same person is always hiding behind it, but that would be too easy. In fact, the UUID is different on each device of the same user, and is also recreated when you reinstall the application to which the extension is attached. Therefore, in order to accurately identify the user and understand that “from here” the payment was accurately made, we decided to use the Yandex account UID.
As we already know, instances of the MSMessage class (messages) can contain multiple text data that is encoded in one web address:
http://yamoney.ru?account=1234567&amount=100&comment=%20
With this line, we immediately provide the recipient’s account number, the transfer amount and an explanation for the user. If the message uses the HTTP or HTTPS URL scheme, then this address will be opened in the macOS browser with a transition to the Yandex.Money mobile web interface. In iOS, the corresponding extension is called instead.
Then we decided to store all the text information in a base64-encoded JSON object:
"?data= TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdC…"
Using JSON allows you to avoid long uncomfortable URLs, and also adds versatility and saves traffic. Thus, the collection of information necessary for translation is performed using service messages with coded URLs.
First, you send a message to Innocent that you want to make a transfer through the Yandex.Money extension interface. From the useful information in this message only the amount of transfer and comment.
{ "status": "created", // "amount": "1000", // "senderId": "dc47160234144e198baa62a3e5edafe5", // "text": " " // }
Innocent tapaet on the message and he also opens iMessage-extension with a proposal to accept the transfer. In the reply message from Innocent, the number of his electronic wallet in Yandex.Money is added, if it is authorized in the main application. If not, using the URL scheme, the extension invokes the application window for authorization.
{ "status": "accepted", "amount": "1000", "to": "4100145388962", // "senderId": "af865bc2575a803dc9726e876f6ee23f", // "text": " " }
Payment confirmation.
As soon as Innocent agrees to transfer, you can send money. If you are already authorized in the application, the PIN code entry screen will open. With it, the iMessage extension will decrypt the payment token of their Keychain application, and then make a payment with it. You can read more about the Yandex.Money API in the special help section .
{ "status": "paid", "amount": "1000", "to": "4100145388962", "senderId": "dc47160234144e198baa62a3e5edafe5", "text": " " }
PIN verification and success screen.
When you need to collect money from several interview participants in iMessage, the initiator creates a message with one amount for each recipient, which contains the number of the target wallet. Recipients need only tapknut on the message and confirm the transfer of the algorithm already familiar to us.
Unlike most Yandex.Money projects, programmers worked primarily on the payment expansion interface. Partly from the interest, partly from the desire to quickly roll out the first version of the payment extension for the internal test. Contrary to popular belief, it turned out very well. At least, UX-designers were outraged only by the selection of colors, which were replaced by those that you see in the screenshots.
In general, at the time of the first development of the payment extension under the iMessage framework, the framework was rather raw, due to which it was necessary to deal with all sorts of dissonances:
It turned out that the extension out of the box is not very friendly in landscape orientation.
And this example is generally the most favorite - for the first time on iOS a diagonal interface.
Since we have touched the interfaces, we’ll discuss in more detail the features of working with GUI extensions iMessage.
The main View Controller for the extension is a class inherited from MSMessagesAppViewController , which displays the interface when the extension is activated. If we want to display other view controller, then the usual calls to present or push will not work here - you need to overlay controllers manually.
To do this, we wrote a small extension to the UIViewController class:
extension UIViewController { func presentChild(_ controller: UIViewController) { addChildViewController(controller) controller.view.frame = view.bounds view.addSubview(controller.view) controller.didMove(toParentViewController: self) } func dismissChild() { willMove(toParentViewController: nil) view.removeFromSuperview() removeFromParentViewController() } }
An auxiliary function of the main controller can be called tracking the life cycle of an extension, for which we use redefined methods like UIApplicationDelegate.
For the expansion of Yandex. Money, we managed three of them:
class MessagesViewController: MSMessagesAppViewController { override func willBecomeActive(with conversation: MSConversation) { super.willBecomeActive(with: conversation) // , . , , . transferButton.isHidden = conversation.remoteParticipantIdentifiers.count > 1 } override func willSelect(_ message: MSMessage, conversation: MSConversation) { super.willSelect(message, conversation: conversation) // present(message: message) } override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) { super.willTransition(to: presentationStyle) // view controller switch presentationStyle { case .compact: presentChild(selectViewController) case .expanded: present(message: activeConversation.selectedMessage) } } ... }
From noteworthy features of building an interface on this, perhaps, everything.
Our script can be used not only for money transfers, but also in other areas where you need to know some information about the addressee. For example, iMessage extensions can be adapted for easy sharing of contacts from social networks or for sending out individual invitations to an event.
What other payment chips, in your opinion, can be useful in the messenger?
Source: https://habr.com/ru/post/323520/
All Articles