ios개발/프로젝트

<스위프트 프로젝트> alamofire와 데이터 파싱 (downcasting)

studying develop 2020. 3. 10. 13:12

 

DowCasting

 

DispatchQueue.global(qos: .userInteractive).async(group: group, execute: { [unowned self] in
            
            Alamofire.request(weatherURL).responseJSON(completionHandler: { (response) in
                
                guard response.result.isSuccess else {
                    group.leave()
                    fatalError("❌ Error, Not Receive Data From Dark SKY Server.")
                }
                
                switch response.response?.statusCode {
                    case .none : print("❌ Error, Not Receive Data From Dark SKY Server.")
                    case .some(_) :
                        guard let result = response.result.value, let json = result as? NSDictionary else { return }
                        guard let list   = json["currently"] as? [String:Any] else { return }
                        
                        self.weatherData.temperature    = list["apparentTemperature"] as! Double
                        self.weatherData.humidity       = list["humidity"] as! Double
                        self.weatherData.visibility     = list["visibility"] as! Double
                        self.weatherData.ozone          = list["ozone"] as! Double
                        self.weatherData.sky            = list["summary"] as! String
                        self.weatherData.icon           = list["icon"] as! String
                }
                group.leave()
            })
        })

 

[https://github.com/ChangYeop-Yang/Study-iOS/blob/master/%5BiOS%5D%20Kakao%20Pay%20Subject/KakaoPaySubject/KakaoPaySubject/Weather%20Files/Weather.swift]

 

이 코드를 완벽히 이해해 보려 한다. alamofire도 사용해보자.

 

일단 문법을 먼저 보면 문법중 중간에

let json = result as? NSDictionary 

as?가 대충 알기로는 형 변환인데, 정확히 알아보았다 [https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

 

DownCasting이다. 

 

맨 첫 문단을 해석해보면

 

실세로 특정 클래스 타입의 변수나 상수는 실제로는 서브 클래스의 객체를 나타내는 것일 수도 있다. 만약 그렇다고 생각된다면 우리는 다운 섭클래스 타입으로 다운 캐스트 할 수 있다. (as? 또는 as!로) 

 

as?는 조건부 형태라고 하는데, 옵셔널 값을 반환한다, as!는 강제로 언랩하여 값을 반환한다. 다운캐스트가 성공할게 확실하면 !를 사용하고 아니면 ?을 사용해라 조건 문으로

 

음 일단 위의 한줄의 코드에서 as?는 이해가 된다. 그런데 result가 뭐길래 NSDictionary로 되지? NSDictionary는 뭐지?

 

 

위키 피디아에서의 Downcasting도 읽었다. [https://en.wikipedia.org/wiki/Downcasting]

 

In class-based programming, downcasting or type refinement is the act of casting a reference of a base class to one of its derived classes..

 

public class Fruit{}  // parent class
public class Apple extends Fruit{}  // child class

public static void main(String args[]) {
    // The following is an implicit upcast:
    Fruit parent = new Apple();
    // The following is a downcast. Here, it works since the variable `parent` is
    // holding an instance of Apple:
    Apple child = (Apple)parent;
}

 

위키의 예시 코드입니다.

 


 

NSDictionary

[https://developer.apple.com/documentation/foundation/nsdictionary]

 

An object representing a static collection of key-value pairs, for use instead of a Dictionary constant in cases that require reference semantics..

 

음 정적 키-밸루 쌍의 집합이라고, 레퍼런스 시멘틱이 필요할때 딕셔너리 대신에 사용한다.

레처런스 시멘틱은 뭐고 정적 집합은 뭐지;

 

근데 내가 궁금한건 json 타입을 딕셔너리 형태로 저렇게 다운 캐스팅 되냐는 건데, json이 스트링으로 들어온건지, 다른 타입이 있는건지 궁금하기도 하다.

 

일단 json에 대한 설명은 여기가 잘되있다. [https://blog.azulpintor.io/entry/json-structure-example]

 

이분 설명에서 내가 간과한 부분이 있는데, object형식과 array형식의 차이이다. 

 

중요한 점은 object 형식은 SET 구조를 가지기 때문에 key값의 중복이 없다는 것.

만약 key값을 중복기재 한다면 절차에 따라 마지막 정의된 값으로 할당받게 된다.

반면에 array형식은 순서화된 COLLECTION 구조로 값이 중복되어도 표현할 수 있다는 점이다.

 

 

플레이 그라운드에서 내가 string을 NSDictionary나 dictionary로 바꿔 보려했다.

 

string에서 NSDictionary가 안되네

 

//이렇게 제이슨을 선언할 수 있네
let json2 = """
{
    "name": "moogii",
    "age": 5,
    "isProgrammer": true,
    "skills": [
        {
            "language": "JAVA",
            "level": 1
        },
        {
            "language": "Python",
            "level": 2
        }
    ],
    "workingDays": ["월", "화", "수", "목", "금", "금", "금"]
}
"""

이렇게 선언해도 된다.

 

String to Dic도 안된다.

 

 

JSONSerialization을 이용해 JSON Parsing이 가능하다고 합니다. [https://mixup.tistory.com/110]

문자열을 data 타입으로 바꾸네

data 타입으로 바꾸고 JSONSerialization.jsonObject를 통해 변환하는데 options에 []은 뭐지? data 타입이랑 option에 대해서 알아보자.

 

[https://developer.apple.com/documentation/foundation/nsstring/1416696-data] 여기에 의하면 data(using:)은 NSData object를 반환한다 한다.

음 NSData를 알아보자. 

Data는 구조체로 메모리에서 바이트 버퍼이다. 데이타 값은 바이트 버퍼를 갖는다. 다양한 소스로 부터 프리 파퓰레이티드??한 버퍼를 만들고 바이트를 추가하거나 제거할 수 있다고..... 

내용물의 필터링과 정렬이 가능하고 또한 다른 버퍼와의 비교가 가능하다한다. 종속되는 바이트들을 조작하고 그들에 대해 iterate 순환할 수 있다함.

 

그럼 일단 데이터로 만들어 보고 다운 캐스팅이 되는지 볼까나

 

string은 데이터로 다운캐스팅이 된다.

 

 

왜인지 모르겠는데 에러가 뜬다.;;

 

일단 JSONSerialization.jsonObject를 알아보자. 

 


 

 

[https://developer.apple.com/documentation/foundation/jsonserialization] 애플 문서이다.

 

Class

JSONSerialization 

An object that converts between JSON and the equivalent Foundation objects.

JSON에서 상응하는 Foundation 객체로 전환해 준다.

 

Declaration

class JSONSerialization : NSObject

Overview

You use the JSONSerialization class to convert JSON to Foundation objects and convert Foundation objects to JSON.

A Foundation object that may be converted to JSON must have the following properties:

파운데이션 객체과 제이슨 양방향으로 전환이 가능하다. 파운데이션 객체는 반드시 최상위 객체에 NSArray나 NSDictionary가 있어야 되고, 모든 객체들은 NSStrnig,NSNumber,NSArray,NSDictionary,nsnull의 인스턴스여야 한다. 모든 딕셔너리 키들은 nsstring의 인스턴스 여야한다. 또는 숫자가 nan이나 무한이면 안된다.

Other rules may apply. Calling isValidJSONObject(_:) or attempting a conversion are the definitive ways to tell if a given object can be converted to JSON data.

다른 규칙들이 있을 수 도 있다. isValidJSonObject()로 전환을 시도해볼 수 있다.

 

Thread Safety

On iOS 7 and later and macOS 10.9 and later JSONSerialization is thread safe.

 

 


Type Method [https://developer.apple.com/documentation/foundation/jsonserialization/1415493-jsonobject]

jsonObject(with:options:)

Returns a Foundation object from given JSON data. 

제이슨 데이타로 부터 파운데이션 객체를 반환한다.

 

class func jsonObject(with data: Data, 
              options opt: JSONSerialization.ReadingOptions = []) throws -> Any

 

함수의 인자를 살펴보면 데이터는 그냥 제이슨 데이터고, 옵션들 [https://developer.apple.com/documentation/foundation/jsonserialization/readingoptions] 여기를 보래서 봤는데 뭔지 모르겠다.;;

 

 

 

이게 도대체 왜 안되지.... 저기 에러가 왜나는지 모르겠다.

 

ㅠㅠ....

 

if let oJsonData = oJsonDataT{
    var oJsonDictionaryT : [String:Any]?
    
    let jdic = try JSONSerialization.jsonObject(with: oJsonData, options: .allowFragments)
    
    print(jdic)
    
    oJsonDictionaryT = try JSONSerialization.jsonObject(with: oJsonData, options: []) as! [String:Any]
    
    if let oJsonDictionary = oJsonDictionaryT
    {
        if let strResultCode = oJsonDictionary["ResultCode"],
        let strDescription = oJsonDictionary["Description"]
        {
            print("ResultCode = \(strResultCode)")
            print("Description = \(strDescription)")
        }
    }
    
    
}

try에 ! 를 빼니까 맞았따......

 


try, try?, try! 차이점

[https://outofbedlam.github.io/swift/2016/03/22/try/]

 

as? as!랑 비슷하다. 에러날 가능성이 없음이 확실하면 try!를 사용하라 합니다.

 

 


그럼 이제 이걸 파싱해보자.

//이렇게 제이슨을 선언할 수 있네
let json2 = """
{
    "name": "moogii",
    "age": 5,
    "isProgrammer": true,
    "skills": [
        {
            "language": "JAVA",
            "level": 1
        },
        {
            "language": "Python",
            "level": 2
        }
    ],
    "workingDays": ["월", "화", "수", "목", "금", "금", "금"]
}
"""

 

 

 

일단 원하는 대로 나온다.

 

codable이랑 jsonSerialization이랑은 뭐가 다른거지 ㅠ!

 


 

 

Alamofire를 사용해보자.