Given that you specifically want to encode individual chars as individual chars (i.e. 8 bits converted to 8 bits), the only requirement that you have is that your encoding function is a bijection -- that is that it never maps two input characters to the same encoded character. As long as you maintain this requirement, you can always calculate an inverse function which restores the original characters.
XOR is one such bijection. An addition (modulo 256) is another bijection. Swapping high and low order nybbles (4 bits) is another option. Swapping every other bit is an option. Any one of these will suffice.
In fact, one can trivially prove that there are precisely
possible ways to encode a character this way, which is 256!. If you exclude the possibility of encoding all characters to themselves, subtract one from this number.
XOR and addition have a particular advantage that they are almost always hardware accelerated -- CPUs can do them in one cycle, with one instruction. This makes them fast and easy. Some CPUs also have a "barrel shift" operator which does a shift, wrapping the bits around to the other side, so on those CPUs you could also use a shift efficiently.
XOR is the most popular for many reasons. It's trivial to understand at a bit level, and has a convenient property that encoding and decoding are precisely the same. It's also technically keyed. While unsigned addition of 128 also encodes/decodes with the same instruction, only one such number works that way.
In the end, XOR is also popular because nobody really cares all that much. If one is merely obsfucating content lightly like this, there's no real advantage to being creative. You go with what is easy. XOR shows up in all the examples, so XOR is what people tend to use. Thus people tend to make examples using XOR. With no real advantage to doing better, XOR kind of wins the day, thanks to that feedback loop.