관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Codable 고급 (url 파싱하여 struct의 property에 매핑하는 방법, query string 파싱, query parameter 파싱) 본문

iOS 응용 (swift)

[iOS - swift] Codable 고급 (url 파싱하여 struct의 property에 매핑하는 방법, query string 파싱, query parameter 파싱)

jake-kim 2024. 3. 11. 01:46

url 파싱

  • 아래와 같은 url이 있을 때, a1_b, a2같은 값을 파싱하는 방법?
"abc://host?a1_b=1&a2=2"
  • 보통 Swift의 Codable을 사용하여 API의 응답값들을 미리 struct로 정의하여 표현하는데 url 파싱도 이렇게 처리가 가능
// Codable을 사용한 API 응답 파싱 예시
struct SomeResponse: Codable {
    let age: Int
    let name: String
}

구현 목적

  • 딥링크를 구현하다보면 여러 url을 처리해야하는데, API의 응닶값처럼 url도 Codable을 준수하는 struct모델을 만들어서 정의되도록 구현하는 것이 목적

ex) 아래와 같은 url이 있을 때, parameter 2개에 대한 모델을 따로 정의하고 싶은 경우?

let url = "abc://host?a1_b=1&a2=2"
  • Codable과 같은 기능을 하는 QueryParameterParsable 프로토콜을 만들고 이 안에 static method인 parse(urlString:)을 구현하여 아래처럼 사용할 수 있도록 구현
struct Model: QueryParameterParsable {
    var a1B: String?
    var a2: String?
}

Model.parse(urlString: url) // Model(a1B: Optional("1"), a2: Optional("2"))
  • 이렇게 구현하면 여러가지 url이 생길 때 그때마다 QueryParameterParsable을 따르도록하고 sturct에 있는 property가 키값, 이 property가 가지고 있는 값이 value로 사용이 가능
  • 명시적으로 모델을 표현하므로 query parameter 구성 파악도 쉬운 장점이 존재

Codable을 활용하여 URl을 파싱하는 아이디어

  • 1. URL을 URLComponents을 사용하여 field, value 쌍을 dictionary로 표현
  • 2. JSONSerialization를 사용하면 dictionary를 jsonData로 만들기가 가능
  • 3. jsonData는 Codable로 decoding 수행이 가능

구현

  • QueryParameterParsable 구현
    • 이 안에서 static method를 활용할 것인데, 이 내부에서 json으로 만들고 json을 decode할 때 필요한 Codable 속성을 명시
public protocol QueryParameterParsable: Codable {}

public extension QueryParameterParsable {
    static func parse(urlString: String) -> Self? {
       ...
    }
}
  • 1. URL을 URLComponents을 사용하여 field, value 쌍을 dictionary로 표현
    static func parse(urlString: String) -> Self? {
        guard let components = URLComponents(string: urlString),
              let queryItems = components.queryItems else {
            return nil
        }
  • 2. JSONSerialization를 사용하면 dictionary를 jsonData로 만들기가 가능
        var parameters: [String: String] = [:]
        for item in queryItems {
            parameters[item.name] = item.value
        }
  • json string에서 key값들은 snake case로 올것이고, struct에 작성하는 property들은 camelCase이기 때문에 decode 속성을 snake case 지정 
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
  • 3. jsonData는 Codable로 decoding 수행
        guard let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: []),
              let decodedSelf = try? decoder.decode(Self.self, from: jsonData) else {
            return nil
        }
        
        return decodedSelf

(전체 코드)

import Foundation

public protocol QueryParameterParsable: Codable {}

public extension QueryParameterParsable {
    static func parse(urlString: String) -> Self? {
        // 1. URL을 URLComponents을 사용하여 field, value 쌍을 dictionary로 표현
        guard let components = URLComponents(string: urlString),
              let queryItems = components.queryItems else {
            return nil
        }
        
        // 2. JSONSerialization를 사용하면 dictionary를 jsonData로 만들기가 가능
        var parameters: [String: String] = [:]
        for item in queryItems {
            parameters[item.name] = item.value
        }
        
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        
        // 3. jsonData는 Codable로 decoding 수행이 가능
        guard let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: []),
              let decodedSelf = try? decoder.decode(Self.self, from: jsonData) else {
            return nil
        }
        
        return decodedSelf
    }
}
  • 이 프로토콜을 준수하는 모델 정의
import Foundation

struct Model: QueryParameterParsable {
    var a1B: String?
    var a2: String?
}

사용)

let url = "abc://host?a1_b=1&a2=2"
if let model = Model.parse(urlString: url) {
    print(model) // Model(a1B: Optional("1"), a2: Optional("2"))
}

* 전체 코드: https://github.com/JK0369/ExParse

 

Comments