Andrei Herford
Andrei Herford

Reputation: 18729

How to correctly urlencode a string in Swift to be decoded uses PHP urldecode?

I would like to send some URL encoded data from a Swift app to a PHP API where the data is decoded again.

While this works fine most of the time, I now became aware that PHP urldecode handles the + sign differently than the Swift addingPercentEncoding and decodes the + to a space.

How can this be avoided?

// Swift
let data = "some+test"
let encodedData = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)  // some+test
sendToMyServer(encodedData)

// PHP
$encodedData = $receivedData;             // some+text
$decodedData = urldecode($encodedData);   // some text

It seems that Swift does not consider the + sign in any of its build in (inverted) char sets:

URLFragmentAllowedCharacterSet  "#%<>[\]^`{|}
URLHostAllowedCharacterSet      "#%/<>?@\^`{|}
URLPasswordAllowedCharacterSet  "#%/:<>?@[\]^`{|}
URLPathAllowedCharacterSet      "#%;<>?[\]^`{|}
URLQueryAllowedCharacterSet     "#%<>[\]^`{|}
URLUserAllowedCharacterSet      "#%/:<>?@[\]^`

Is there a reason why PHP handles the + sign in this way?

What is the "correct" way to solve this? I assume I could simple define a custom char set which includes the + sign, but I am not sure if this might lead to other unexpected side effects. Is this safe or are there other differences in PHP urldecode which needs to be considered?

Upvotes: 0

Views: 382

Answers (1)

Larme
Larme

Reputation: 26026

For the reason:

Doc of CharacterSet.urlHostAllowed:

Returns the character set for characters allowed in a host URL subcomponent.

If we then go to implicitly linked doc of URLComponents

This structure parses and constructs URLs according to RFC 3986. Its behavior differs subtly from that of the URL structure, which conforms to older RFCs. However, you can easily obtain a URL value based on the contents of a URLComponents value or vice versa.

The doc of PhP urldecode says:

Plus symbols ('+') are decoded to a space character.

The urlencode says:

This differs from the » RFC 3986 encoding (see rawurlencode()) in that for historical reasons, spaces are encoded as plus (+) signs.

So iOS uses the RFC 3986 and PhP doesn't.

Solution:

Use the same encoding/decoding, by changing it on iOS or in the server.

For the server changes:

To use the same as iOS on the server side, use rawurldecode (or rawurlencode if needed), as it says:

rawurldecode() does not decode plus symbols ('+') into spaces. urldecode() does.

and

rawurlencode — URL-encode according to RFC 3986

For the iOS changes:

See Sajjad's answer

Upvotes: 1

Related Questions