By MegaCookie


2014-07-03 10:56:02 8 Comments

If I encode a string like this:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

it doesn't escape the slashes /.

I've searched and found this Objective C code:

NSString *encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
                        NULL,
                        (CFStringRef)unencodedString,
                        NULL,
                        (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                        kCFStringEncodingUTF8 );

Is there an easier way to encode an URL and if not, how do I write this in Swift?

14 comments

@jaskiratjd 2019-03-05 11:05:19

let Url = URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")

@Leo Dabus 2017-04-27 21:27:38

You can use URLComponents to avoid having to manually percent escape your query string:

let scheme = "https"
let host = "www.google.com"
let path = "/search"
let queryItem = URLQueryItem(name: "q", value: "Formula One")


var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItem]

if let url = urlComponents.url {
    print(url)   // "https://www.google.com/search?q=Formula%20One"
}

extension URLComponents {
    init(scheme: String = "https",
         host: String = "www.google.com",
         path: String = "/search",
         queryItems: [URLQueryItem]) {
        self.init()
        self.scheme = scheme
        self.host = host
        self.path = path
        self.queryItems = queryItems
    }
}

let query = "Formula One"
if let url = URLComponents(queryItems: [URLQueryItem(name: "q", value: query)]).url {
    print(url)  // https://www.google.com/search?q=Formula%20One
}

@Asa 2017-05-18 14:55:55

This answer needs more attention, as there are issues with all of the other ones (though to be fair they may have been best practice at the time).

@Sulthan 2018-03-10 08:15:58

Sadly, URLQueryItem does not always encode correctly. For example, Formula+One would be encoded to Formula+One, which would be decoded to Formula One. Therefore be cautious with the plus sign.

@ajzbc 2019-02-21 20:54:13

Swift 4.2

A quick one line solution. Replace originalString with the String you want to encode.

var encodedString = originalString.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]{} ").inverted)

Online Playground Demo

@zaph 2014-07-03 11:05:01

Swift 3

In Swift 3 there is addingPercentEncoding

let originalString = "test/test"
let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(escapedString!)

Output:

test%2Ftest

Swift 1

In iOS 7 and above there is stringByAddingPercentEncodingWithAllowedCharacters

var originalString = "test/test"
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
println("escapedString: \(escapedString)")

Output:

test%2Ftest

The following are useful (inverted) character sets:

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

If you want a different set of characters to be escaped create a set:
Example with added "=" character:

var originalString = "test/test=42"
var customAllowedSet =  NSCharacterSet(charactersInString:"=\"#%/<>[email protected]\\^`{|}").invertedSet
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(customAllowedSet)
println("escapedString: \(escapedString)")

Output:

test%2Ftest%3D42

Example to verify ascii characters not in the set:

func printCharactersInSet(set: NSCharacterSet) {
    var characters = ""
    let iSet = set.invertedSet
    for i: UInt32 in 32..<127 {
        let c = Character(UnicodeScalar(i))
        if iSet.longCharacterIsMember(i) {
            characters = characters + String(c)
        }
    }
    print("characters not in set: \'\(characters)\'")
}

@Bryan Chen 2014-07-03 11:59:03

You are right. I misunderstand the document...

@MegaCookie 2014-07-03 17:44:17

And if I want to add the equal sign too?

@zaph 2014-07-05 03:51:28

Add a custom character set, I added an example.

@thatidiotguy 2014-09-30 20:19:35

Is no one else completely flabbergasted at how long this code is to do this? I mean that method name is already hell of long, even without choosing the allowed character set.

@zaph 2014-09-30 20:37:18

No, I favor understandability over short terse naming. Autocomplete takes the pain out. stringByAddingPercentEncodingWithAllowedCharacters() leaves little doubt about what it does. Interesting comment considering how long the word: "flabbergasted" is.

@Julio Garcia 2016-01-30 16:54:25

stringByAddingPercentEncodingWithAllowedCharacters(.URLHostA‌​llowedCharacterSet()‌​) Does not encode all character properly Bryan Chen's answer is a better solution.

@zaph 2016-01-30 16:58:53

@JulioGarcia either pick a character set based on the portion of the URL you are using or create a custom character set that matches your needs. Look at the customAllowedSet example.

@Julio Garcia 2016-01-30 17:01:50

Thanks Zaph, I just noticed that function it's been deprecated in iOS 9

@Akash Kava 2016-03-02 09:25:59

@zaph What about &?

@zaph 2016-03-02 13:05:35

@AkashKava pick a character set that meets your needs of use charactersInString: to create a custom character set.

@Akash Kava 2016-03-02 15:28:58

@zaph I added & to character set of URLQueryAllowedCharacterSet and I got each character encoded. Checked with iOS 9, looks like buggy, I went with @bryanchen's answer, it works well !!

@user3246173 2016-05-19 16:25:09

Why is Apple putting this method in the String class and not on NSURL?

@zaph 2016-05-19 21:34:51

That the method takes a string and returns a string is my best guess.

@Ben Carroll 2016-06-27 06:36:59

And of course, they've cleaned up the API a bit now. For example: let myEncodedString = myString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)

@Kwnstantinos Natsios 2016-10-04 15:22:56

Your code doesn't encode ! and some other special chacacters... why?

@zaph 2016-10-04 15:36:05

Apple supplies several charactersets directed toward URL encoding. If you need to encode a different characterset than those supplied by Apple use the example of customAllowedSet.

@Aaron Brager 2017-11-12 17:05:25

The answer below that uses URLComponents and URLQueryItem is much cleaner IMO.

@Jack 2018-03-23 11:15:32

I used CharacterSet(charactersIn: "=&").inverted Thanks @zaph

@Vrushal Raut 2019-01-18 06:55:39

SWIFT 4.2

Sometimes this happened just because there is space in slug OR absence of URL encoding for parameters passing through API URL.

let myString = self.slugValue
                let csCopy = CharacterSet(bitmapRepresentation: CharacterSet.urlPathAllowed.bitmapRepresentation)
                let escapedString = myString!.addingPercentEncoding(withAllowedCharacters: csCopy)!
                //always "info:hello%20world"
                print(escapedString)

NOTE : Don't forget to explore about bitmapRepresentation.

@CartoonChess 2018-11-06 17:18:13

This is working for me in Swift 4.2. The usage case is taking a URL from the clipboard or similar which may already have escaped characters but which also contains Unicode characters which could cause URL(string:) to fail.

func encodedUrl(from string: String) -> URL? {
    // Remove preexisting encoding
    guard let decodedString = string.removingPercentEncoding,
        // Reencode, to revert decoding while encoding missed characters
        let percentEncodedString = decodedString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
            // Coding failed
            return nil
    }
    // Create URL from encoded string, or nil if failed
    return URL(string: percentEncodedString)
}

let urlText = "https://www.example.com/폴더/search?q=123&foo=bar&multi=eggs+and+ham&hangul=한글&spaced=lovely%20spam&illegal=<>"
let url = encodedUrl(from: urlText)

Value of url at the end: https://www.example.com/%ED%8F%B4%EB%8D%94/search?q=123&foo=bar&multi=eggs+and+ham&hangul=%ED%95%9C%EA%B8%80&spaced=lovely%20spam&illegal=%3C%3E

Note that both %20 and + spacing are preserved, Unicode characters are encoded, and the %20 in the original urlText is not double encoded.

Caveat: This is a shortcut, as it's using .urlQueryAllowed on the entire URL; it also assumes the URL was already working in a browser. Use wisely! I'm also open to suggestions (I stopped short of chopping the entire URL up with URLComponents, applying individual character sets, and reassembling).

@AJP 2016-09-29 10:16:23

Swift 4 (not tested - please comment if it works or not. Thanks @sumizome for suggestion)

var allowedQueryParamAndKey = NSCharacterSet.urlQueryAllowed
allowedQueryParamAndKey.remove(charactersIn: ";/?:@&=+$, ")
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)

Swift 3

let allowedQueryParamAndKey =  NSCharacterSet.urlQueryAllowed.remove(charactersIn: ";/?:@&=+$, ")
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)

Swift 2.2 (Borrowing from Zaph's and correcting for url query key and parameter values)

var allowedQueryParamAndKey =  NSCharacterSet(charactersInString: ";/?:@&=+$, ").invertedSet
paramOrKey.stringByAddingPercentEncodingWithAllowedCharacters(allowedQueryParamAndKey)

Example:

let paramOrKey = "https://some.website.com/path/to/page.srf?a=1&b=2#top"
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)
// produces:
"https%3A%2F%2Fsome.website.com%2Fpath%2Fto%2Fpage.srf%3Fa%3D1%26b%3D2%23top"

This is a shorter version of Bryan Chen's answer. I'd guess that urlQueryAllowed is allowing the control characters through which is fine unless they form part of the key or value in your query string at which point they need to be escaped.

@Marián Černý 2018-03-09 20:29:19

I like the Swift 3 solution, but it does not work for me in Swift 4: "Cannot use mutating member on immutable value: 'urlQueryAllowed' is a get-only property".

@sumizome 2018-09-25 22:45:46

@MariánČerný just make the CharacterSet mutable (with var) and then call .remove on it in a second step.

@Marián Černý 2018-03-12 08:56:23

Swift 4

To encode a parameter in URL I find using .alphanumerics character set the easiest option:

let encoded = parameter.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
let url = "http://www.example.com/?name=\(encoded!)"

Using any of the standard Character Sets for URL Encoding (like URLQueryAllowedCharacterSet or URLHostAllowedCharacterSet) won't work, because they do not exclude = or & characters.

Note that by using .alphanumerics it will encode some characters that do not need to be encoded (like -, ., _ or ~ -– see 2.3. Unreserved characters in RFC 3986). I find using .alphanumerics simpler than constructing a custom character set and do not mind some additional characters to be encoded. If that bothers you, construct a custom character set as is described in How to percent encode a URL String, like for example:

var allowed = CharacterSet.alphanumerics
allowed.insert(charactersIn: "-._~") // as per RFC 3986
let encoded = parameter.addingPercentEncoding(withAllowedCharacters: allowed)
let url = "http://www.example.com/?name=\(encoded!)"

Warning: The encoded parameter is force unwrapped. For invalid unicode string it might crash. See Why is the return value of String.addingPercentEncoding() optional?. Instead of force unwrapping encoded! you can use encoded ?? "" or use if let encoded = ....

@Alessandro Ornano 2018-03-07 10:07:55

Swift 4:

It depends by the encoding rules followed by your server.

Apple offer this class method, but it don't report wich kind of RCF protocol it follows.

var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!

Following this useful tool you should guarantee the encoding of these chars for your parameters:

  • $ (Dollar Sign) becomes %24
  • & (Ampersand) becomes %26
  • + (Plus) becomes %2B
  • , (Comma) becomes %2C
  • : (Colon) becomes %3A
  • ; (Semi-Colon) becomes %3B
  • = (Equals) becomes %3D
  • ? (Question Mark) becomes %3F
  • @ (Commercial A / At) becomes %40

In other words, speaking about URL encoding, you should following the RFC 1738 protocol.

And Swift don't cover the encoding of the + char for example, but it works well with these three @ : ? chars.

So, to correctly encoding each your parameter , the .urlHostAllowed option is not enough, you should add also the special chars as for example:

encodedParameter = parameter.replacingOccurrences(of: "+", with: "%2B")

Hope this helps someone who become crazy to search these informations.

@Marián Černý 2018-03-09 19:38:50

Your implementation is completely wrong. How would a parameter "věž" be encoded?

@Alessandro Ornano 2018-03-10 07:37:30

Thank you, I've update my answer.

@iHTCboy 2017-04-28 14:34:45

Swift 3:

let originalString = "http://www.ihtc.cc?name=htc&title=iOS开发工程师"

1. encodingQuery:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)

result:

"http://www.ihtc.cc?name=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88" 

2. encodingURL:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

result:

"http:%2F%2Fwww.ihtc.cc%3Fname=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88"

@Akshay Phulare 2018-01-02 13:38:33

I used first solution but I want to my text back, like iOS开发工程师.

@Naveed Ahmad 2018-11-24 19:36:33

.urlQueryAllowed worked for me, thanks

@Sulthan 2019-01-03 14:18:43

Using urlHostAllowed for encoding query parameters in incorrect because it won't encode ?, = and +. When encoding query parameters, you have to encode parameter name and value separately and correctly. This won't work in a general case.

@Bharath 2019-01-06 10:31:05

@Sulthan.. Did you find any solution / alternative for urlHostAllowed

@Sulthan 2019-01-06 10:44:46

@Bharath Yes, you have to build a character set by yourself, e.g. stackoverflow.com/a/39767927/669586 or just use URLComponents.

@Gaurav Singla 2016-06-06 09:23:08

This one is working for me.

func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? {

    let unreserved = "*-._"
    let allowed = NSMutableCharacterSet.alphanumericCharacterSet()
    allowed.addCharactersInString(unreserved)

    if plusForSpace {
        allowed.addCharactersInString(" ")
    }

    var encoded = stringByAddingPercentEncodingWithAllowedCharacters(allowed)

    if plusForSpace {
        encoded = encoded?.stringByReplacingOccurrencesOfString(" ", withString: "+")
    }
    return encoded
}

I found above function from this link: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/.

@BadPirate 2015-01-02 00:39:59

Had need of this myself, so I wrote a String extension that both allows for URLEncoding strings, as well as the more common end goal, converting a parameter dictionary into "GET" style URL Parameters:

extension String {
    func URLEncodedString() -> String? {
        var escapedString = self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
        return escapedString
    }
    static func queryStringFromParameters(parameters: Dictionary<String,String>) -> String? {
        if (parameters.count == 0)
        {
            return nil
        }
        var queryString : String? = nil
        for (key, value) in parameters {
            if let encodedKey = key.URLEncodedString() {
                if let encodedValue = value.URLEncodedString() {
                    if queryString == nil
                    {
                        queryString = "?"
                    }
                    else
                    {
                        queryString! += "&"
                    }
                    queryString! += encodedKey + "=" + encodedValue
                }
            }
        }
        return queryString
    }
}

Enjoy!

@Sam 2015-11-26 14:34:46

This does not encode the '&' sign. Using a '&' in a parameter will f*** up the querystring

@Marián Černý 2018-03-09 20:20:18

This is wrong, it does not encode & or = in parameters. Check my solution instead.

@Hong Wei 2016-09-29 10:34:54

Swift 3:

let allowedCharacterSet = (CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted)

if let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) {
//do something with escaped string
}

@AJP 2016-09-29 15:49:35

You need to include ` ` (space) in the string of characters

@Mani 2017-04-27 06:28:58

You also need to include ^

@Bryan Chen 2014-07-03 11:05:13

Everything is same

var str = CFURLCreateStringByAddingPercentEscapes(
    nil,
    "test/test",
    nil,
    "!*'();:@&=+$,/?%#[]",
    CFStringBuiltInEncodings.UTF8.rawValue
)

// test%2Ftest

@Kreiri 2014-07-03 11:16:55

You didn't .bridgeToOvjectiveC() second argument and didn't get "Cannot convert the expression's type 'CFString!' to type 'CFString!'" ?

@Bryan Chen 2014-07-03 11:55:20

@Kreiri Why it is needed? Both playground and REPL are happy with my code.

@Kreiri 2014-07-03 12:02:31

Mine aren't :/ (beta 2)

@Sam 2015-11-26 14:34:05

This is a better answer as it encodes the & correctly.

Related Questions

Sponsored Content

24 Answered Questions

[SOLVED] How do I make an attributed string using Swift?

18 Answered Questions

[SOLVED] #pragma mark in Swift?

  • 2014-06-03 14:05:56
  • Arbitur
  • 203093 View
  • 873 Score
  • 18 Answer
  • Tags:   swift

20 Answered Questions

[SOLVED] How do I convert a Swift Array to a String?

  • 2014-09-13 19:48:08
  • Troy
  • 193501 View
  • 306 Score
  • 20 Answer
  • Tags:   ios arrays swift

10 Answered Questions

[SOLVED] Java URL encoding of query string parameters

15 Answered Questions

[SOLVED] How to call Objective-C code from Swift

  • 2014-06-02 20:05:42
  • David Mulder
  • 260659 View
  • 894 Score
  • 15 Answer
  • Tags:   objective-c swift

32 Answered Questions

[SOLVED] Split a String into an array in Swift?

7 Answered Questions

[SOLVED] Ruby url encoding string

30 Answered Questions

[SOLVED] Loading/Downloading image from URL on Swift

24 Answered Questions

[SOLVED] HTTP URL Address Encoding in Java

  • 2009-04-07 03:28:22
  • Sudhakar R
  • 343150 View
  • 350 Score
  • 24 Answer
  • Tags:   java http urlencode

5 Answered Questions

[SOLVED] When to encode space to plus (+) or %20?

  • 2010-04-20 20:55:31
  • Muhammad Hewedy
  • 203974 View
  • 416 Score
  • 5 Answer
  • Tags:   urlencode

Sponsored Content