The first reason is that combining presence events into a presence set is not completely trivial to implement. There is a detailed protocol that must be followed to correctly merge events into the presence set, designed so that (for example) events can commute (be applied in any order), synthetic leave events are treated correctly, members are identified by both clientId and connectionId, and so on. If subscribing to presence from a realtime client library, the client library will handle this for you.
Another reason is that there is no synchronization mechanism. If the recipient server goes down or fails to process a message, you will then be permanently out of sync, and you will not know it. Ably will retry a webhook if we notice it has been rejected (timed out or has a non-2xx status code), but only within limits: sufficiently old messages that have been retried a few times will be discarded, and there are limits on e.g. the number of messages per post body and the total number of messages per second you can send through webhooks. (Capping or limiting is logged to the log channel, and so visible to you if you are watching the dev console or have any rules set on the log channel).
Something like Reactor queues would be somewhat better than webhooks for this; the consumer can ack a message only once it has processed it, and if the consumer dies, unacked messages will be requeued, which makes it significantly more reliable than webhooks. See our blog post on this for more. But while a message queue is better than a webhook, it still has no synchronization mechanism.
Our realtime client libraries have a built-in synchronization protocol (either client or server can initiate a sync of the presence set at any time if there is a loss of channel continuity), ensuring that the presence set you see is synced and up to date at all times the client library is connected. That is a level of reliability that is not available by maintaining a presence set using a Reactor rule.
So if you need an always-up-to-date copy of a presence set, our recommendation is that you use one of our Realtime client libraries and attach to that channel.
If you do decide to use a reactor rule, you can pass each array of presence messages as they arrive through PresenceMessage#fromEncodedArray, to translate the numerical presence action into a string, and decode the data payload (if any) back into the same datatype that it was sent in (or an equivalent in each client library’s language). This is especially useful if you are using encryption; you can pass your encryption key to the method and it will decrypt the datafor you.