Swift Json 처리방법

sanghun Lee·2021년 1월 11일
0

Today I Learned

목록 보기
62/66
post-thumbnail

왜 찾게 되었나 ?..

일하고 있는 팀에서 새로 진행중인 프로젝트때문에 swift를 만지게 되었다.
웹뷰를 기반으로 하여 native코드로 몇가지 처리를 해준다고만 들었는데

가지수가 꽤 많은 것 같다.

역시 사람말은 믿으면 안된다.

여튼 프로젝트를 진행하면서 기존 안드로이드 앱의 구현을 위해 작동되고 있는 API를 활용하고 있다.

메인 페이지의 Data 와 유저의 상태에 따른 token값 변경으로 다른 동영상 데이터를 띄워줘야하는 그런 작업을 진행중이다.

그리하여 커스터마이즈된 데이터는 json형태로 받게 된다.

안타깝게도 기존의 회사에서 급했는지 jQuery,php조합으로 웹뷰가 구성이 되어있었고.. 따라서 native언어에서 json을 받아서 웹뷰가 구성되어있는 Html로 넘겨주는 것이 로직의 중복코드를 피할 수 있는 유일한 길이 되었다.

그래서 몇가지를 검색하고 삽질하였던 것을 기록하여본다 허허허..

1. Encode & Decode

    private func parse(jsonData: Data){
        do{
            //decode
            let mdata = try JSONDecoder().decode(getDataMeta.self, from: jsonData)
            //encode
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let newMdata = try? encoder.encode(jsonData)
            if let newMdata = newMdata, let MString = String(data: newMdata, encoding: .utf8){
                print("newMdata", MString)
            }
            //serialize
            let jsonResponse = try JSONSerialization.jsonObject(with: jsonData, options: [])

            self.jsonDataFrom = mdata
            
//            print("JSONDecoder", mdata)
            print("JSONSerialization", mdata)
        } catch{
            print("decodeError: \(error)")
        }
    }

API로 부터 받는 데이터를 파싱하기 위해서 parse라는 함수를 별개로 만들어서 처리하려고 했다.

이것저것 구글링을 해보며 Encoding과 Decoding을 해보고 공식문서를 통해 차이점을 읽어보고 실행을 하려 하였으나 안타까운 사태가 많이 발생했다.

맞다 삽질의 시작이었다.

Encoding을하니 아래와 같은 사진이 뜬다.

애초에 json파일을 출력할때 특별한 값을 설정해주지 않으면 "1004byte"와 같이 byte값이 전달된다
이를 설정을 통해 문자열로 변경하면 아래와 같은 난해한 문자가 생긴다 :)

            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let newMdata = try? encoder.encode(jsonData)
            if let newMdata = newMdata, let MString = String(data: newMdata, encoding: .utf8){
                print("newMdata", MString)
            }

encoder라는 변수에 JSONEncoder()를 담고 outputForamtting을 설정해 이쁘게 나타내보려 했으나 실패했고 .utf8설정을 통해 변경해주었다.

"eyJwcm9maWxlIjp7ImlkIjpudWxsLCJuYW1lIjpudWxsLCJwcm92aWRlciI6bnVsbCwidG9rZW4iOm51bGwsImVtYWlsIjpudWxsLCJnZW5kZXIiOiJtYWxlIiwidGh1bWIiOiJhc3NldHNcL2ltYWdlc1wvcHJvZmlsZV9zbWFsZS5wbmciLCJuZXdzIjoiMCJ9LCJjb250ZW50cyI6eyJkYWJpc3UiOnsiazEiOnsiZGF0YSI6W3siaWQiOiI2MzU0IiwidGl0bGUiOiIxXHViMmU4XHVhY2M0IiwidXJsIjoiZGFiaXN1X2sxXzEuaHRtbCIsInRodW1iIjoiYXNzZXRzXC9pbWFnZXNcL2RhYmlzdVwvazEtMS5wbmciLCJsb2NrIjowfSx7ImlkIjoi
..."

Decoding을하면

아래와 같이 나오는데 자세히 보면 nil타입이 정의되어 버리고 제대로된 JSON형태가 나오지 않게 된다. (컬리브레이스는 어디로..)

JSONDecoder getDataMeta(profile: downtest.profileMetadata(id: nil, name: nil, provider: nil, token: nil, email: nil, gender: "male", thumb: "assets/images/profile_smale.png", news: "0"), contents: downtest.contentsUpperMeta(abc: Optional(downtest.kDataType(k1: Optional(downtest.imageType(data: [downtest.idType(id: "6354", title: "abc", url: "abc.html", thumb: "assets/images/abc/k1-1.png", lock: 0), downtest.idType(id: "6355", title: "2단계", url: "abc.html", thumb: "assets/images/abc/k1-2.png", lock: 1),
...)

이 상태로 Javascript로 넘기게 된다면 parsingerror를 맞이할 수 있다.
이는 swift내부에서 사용할 수 있기 위해 decode가 되어버린 상태이기 때문이라 보면 된다.

unexpected token .... 이라는 지겹고도 지겨운 에러를..

2. Serialize

코드에서 보이듯이 Struct에 대한 처리를 하나하나 다 해주지 않았기 때문에 적합한 형태가 나오지 않는다.
그리고 Serialize기능 자체가 예전에 주로 사용되었고 최근에는 이의 불편함을 해소하기 위해서 JSONEncoder와 같은 기능이 나왔으니 굳이 사용하지 말자.

여러 예제를 보니 보통할일이 아니다..(회문 돌고 struct설정해주고 뭐 ..)

JSONSerialization getDataMeta(profile: downtest.profileMetadata(id: nil, name: nil, provider: nil, token: nil, email: nil, gender: "male", thumb: "assets/images/profile_smale.png", news: "0"), contents: downtest.contentsUpperMeta(abc: Optional(downtest.kDataType(k1: Optional(downtest.imageType(data: [downtest.idType(id: "6354", title: "1단계", url: "avc.html", thumb: "assets/images/avc/k1-1.png", lock: 0), downtest.idType(id: "6355", title: "2단계", url: "abc.html", thumb: "assets/images/abc/k1-2.png", lock: 1), downtest.idType(id: "6357", title: "3단계", url: "abc.html", thumb: "assets/images/abc/k1-3.png", lock: 1),

3. 그래서 해결 방법..

    //loadData with token for getting Json
    public func loadData(token:String){
        guard let url = URL(string:"http://abc.def.com/api/getdata?token=\(token)") else{return}
        
        let task = URLSession.shared.dataTask(with: url){(data, response, error) in
            guard let dataResponse = data,
                  error == nil else{
                print(error?.localizedDescription ?? "Response Error")
                return
            }
            do{
                self.StringJson = try String(contentsOf: url)
                
                self.parse(jsonData: dataResponse)
                print(self.StringJson)

            } catch{
                print(error)
            }
        }
        task.resume()
    }

삽질과 삽질을 통해 발견한 방법이다.

                self.StringJson = try String(contentsOf: url)

애초에 url로부터 들어오는 데이터의 contents.. 즉, 그냥 들어오는 contents를 곧바로 String타입으로 변경시켜서 html로 넘겨주면 되는 일이었다.

따로 JSON.parse()라던지의 절차도 필요없다..

정리

  • Encode와 Decode는 Swift내부에서 Json형태의 데이터를 가공하여 사용하기 위한 방안이다
  • Serialize는 예전방식이기도 하고 struct의 형태를 반복문을 돌며 다 처리해주어야 하기 때문에 별로다.
  • 웹뷰는 SPA로 만들자
  • Swift 의 Struct는 좋은데 싫다
  • Swift에서 JSON을 받아 곧바로 웹뷰의 html로 넘기고 싶다면 decode, encode와 같은 과정은 불필요하다.

참고한 블로그

profile
알고리즘 풀이를 담은 블로그입니다.

0개의 댓글