Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decode URLs with special characters in Swift

An API I work with provides URL links, that can contain special characters like "http://es.dbpedia.org/resource/Análisis_de_datos" (the letter "á" inside).

It's an absolutely valid URL, however, if a decodable class contains an optional URL? variable, it can't be decoded.

I can change URL? to String? in my class and have a computed variable with something like URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) but perhaps there is a more elegant solution.

To reproduce in Playground:

struct Container: Encodable {
    let url: String
}

struct Response: Decodable {
    let url: URL?
}

let container = Container(url: "http://es.dbpedia.org/resource/Análisis_de_datos")

let encoder = JSONEncoder()
let encodedData = try encoder.encode(container)
    
let decoder = JSONDecoder()
let response = try? decoder.decode(Response.self, from: encodedData)
// response == nil, as it can't be decoded.

let url = response?.url 
like image 582
Alexey Chekanov Avatar asked Oct 24 '25 18:10

Alexey Chekanov


1 Answers

There are multiple way to overcome this, but I think using a property wrapper is probably the most elegant:

@propertyWrapper
struct URLPercentEncoding {
   var wrappedValue: URL
}

extension URLPercentEncoding: Decodable {
   public init(from decoder: Decoder) throws {
      let container = try decoder.singleValueContainer()
        
      if let str = try? container.decode(String.self),
         let encoded = str.addingPercentEncoding(
                              withAllowedCharacters: .urlFragmentAllowed),
         let url = URL(string: encoded) {

         self.wrappedValue = url

      } else {
         throw DecodingError.dataCorrupted(
            .init(codingPath: container.codingPath, debugDescription: "Corrupted url"))
      }
   }
}

Then you could use it like so without the consumer of this model having to know anything about it:

struct Response: Decodable {
    @URLPercentEncoding let url: URL
}
like image 186
New Dev Avatar answered Oct 26 '25 07:10

New Dev