URL에 한글이 들어갈때
URL(string:)
네트워크 호출시 서버에서 URL타입을 내려줄 때 리소스 등에 종종 한글이 포함된 채로 내려주는 경우가 있다.
URL은 아스키코드 기반이기 때문에 그대로 URL(string:)이 nil이 되어 버린다. 그러므로 적절하게 인코딩해주어야 하는데, String의 메소드인 addingPercentEncoding(withAllowedCharacters:)에 .urlQueryAllowed를 넣어서 인코딩하면 된다.
let urlString = "https://네이버.com"
let url: URL = URL(string: urlString) // nil
if let encodedURL = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
let url: URL = URL(string: urlString) // https://%EB%84%A4%EC%9D%B4%EB%B2%84.com
}
String에서 URL을 만들때 매번 해줄 수 없으니 디코딩 시에 바로 퍼센트인코딩되도록 프로퍼티 래퍼를 만들었다.
@propertyWrapper
struct PercentEncoded: Decodable {
private var encodedURL: URL?
var wrappedValue: URL? {
get { self.encodedURL }
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let urlString = try container.decode(String.self)
if #available(iOS 17, *) {
self.encodedURL = URL(string: urlString)
} else {
if let encodedURLString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
self.encodedURL = URL(string: encodedURLString)
} else {
self.encodedURL = nil
}
}
}
}
/// 사용 예시
struct Response: Decodable {
@PercentEncoded var url: URL?
}
iOS 17 추가
iOS 17부터는 URL(string:) 생성자의 동작이 달라졌다. RFC 3986을 따르게 되면서 자동으로 퍼센트인코딩이 된다.
열어보면 내부값이 다른데, UTF-8을 직접 인코딩하는게 아니라 xn--으로 시작하는 Punycode로 인코딩된다.
String으로부터 직접 디코딩하기 때문에 사실 상관없지만 표준에 가깝도록 분기쳐 놓았다. 모두 iOS 17이상을 쓰게되면 그냥 URL타입으로 직접 넣어도 알아서 잘 디코딩될것 같다.
let urlString = "https://네이버.com"
if #available(iOS 17, *) {
URL(string: urlString) // https://xn--950bt9s8xi.com
} else {
URL(string: urlString) // nil
if let encodedURL = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
URL(string: urlString) // https://%EB%84%A4%EC%9D%B4%EB%B2%84.com
}
}