It depends, but a flexible approach might look like this. I'll focus on your protocol here—the rest is in the Core Bluetooth docs.
The peripheral publishes an app-specific service, most likely on a UUID that you generated randomly.
Underneath that service it publishes a read/notify characteristic, which will provide an asynchronous response to requests (a different random UUID).
The peripheral also publishes a writeable characteristic for requests.
When the central connects, it discovers both characteristics, subscribes to the "result" characteristic, and writes to the "request" characteristic when it needs to.
These writes and notifications have a small binary data payload of your choice. Typically iOS devices will let you do ~185 bytes at a time so if your requests and responses are short you can insert them directly. If they're bigger you'll have a bit more work to split them up.
Possible simplifications if your app allows it:
If requests can be completed instantly, the central could write a request and then immediately read a response from a second characteristic, without messing about with notifications.
If you have a small fixed variety of requests, you can add a characteristic for each one. Then you know what type of request it is already without parsing the data.
If your requests don't need any parameters or data payload, simply trying to read a specific characteristic can be treated as a request. If the peripheral can provide the response synchronously, it can calculate and return it immediately. This is a very simple workflow to implement.