Swift - encode URL

后端 未结 17 2159
無奈伤痛
無奈伤痛 2020-11-21 22:20

If I encode a string like this:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

it does

17条回答
  •  失恋的感觉
    2020-11-21 22:54

    This is working for me in Swift 5. 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 URLComponents or URL(string:) to fail.

    First, create a character set that includes all URL-legal characters:

    extension CharacterSet {
    
        /// Characters valid in at least one part of a URL.
        ///
        /// These characters are not allowed in ALL parts of a URL; each part has different requirements. This set is useful for checking for Unicode characters that need to be percent encoded before performing a validity check on individual URL components.
        static var urlAllowedCharacters: CharacterSet {
            // Start by including hash, which isn't in any set
            var characters = CharacterSet(charactersIn: "#")
            // All URL-legal characters
            characters.formUnion(.urlUserAllowed)
            characters.formUnion(.urlPasswordAllowed)
            characters.formUnion(.urlHostAllowed)
            characters.formUnion(.urlPathAllowed)
            characters.formUnion(.urlQueryAllowed)
            characters.formUnion(.urlFragmentAllowed)
    
            return characters
        }
    }
    

    Next, extend String with a method to encode URLs:

    extension String {
    
        /// Converts a string to a percent-encoded URL, including Unicode characters.
        ///
        /// - Returns: An encoded URL if all steps succeed, otherwise nil.
        func encodedUrl() -> URL? {        
            // Remove preexisting encoding,
            guard let decodedString = self.removingPercentEncoding,
                // encode any Unicode characters so URLComponents doesn't choke,
                let unicodeEncodedString = decodedString.addingPercentEncoding(withAllowedCharacters: .urlAllowedCharacters),
                // break into components to use proper encoding for each part,
                let components = URLComponents(string: unicodeEncodedString),
                // and reencode, to revert decoding while encoding missed characters.
                let percentEncodedUrl = components.url else {
                // Encoding failed
                return nil
            }
    
            return percentEncodedUrl
        }
    
    }
    

    Which can be tested like:

    let urlText = "https://www.example.com/폴더/search?q=123&foo=bar&multi=eggs+and+ham&hangul=한글&spaced=lovely%20spam&illegal=<>#top"
    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#top

    Note that both %20 and + spacing are preserved, Unicode characters are encoded, the %20 in the original urlText is not double encoded, and the anchor (fragment, or #) remains.

    Edit: Now checking for validity of each component.

提交回复
热议问题