The receive(minimumIncompleteLength:maximumLength:completion:) has a lot of options and it works with many different protocols. Given that, coming up with a definitive table of what it will do in every possible case is hard, and I’ve never seen that officially documented in sufficient detail [1]. Given that, I generally stick with protocol-specific recipes: For stream-based protocols, like TCP, I use receive(minimumIncompleteLength:maximumLength:completion:) and then, in the completion handler, I process: The data first, if there is any Then the error, if there is one For UDP, I always use receiveMessage(completion:). This is a great option because UDP datagrams have a limited size that easily fits in memory. For message-based protocols without that convenient limit, things get tricky. You have to use receive(minimumIncompleteLength:maximumLength:completion:) but you then need to worry about the isComplete Boolean and potentially the context. Fortunately, these cases are rare [2]. Notably, this approach is co