[최종 수정일 : 2018.05.04]

원문 : https://www.raywenderlich.com/188427/alamofire-tutorial-getting-started-3

Alamofire Tutorial: Getting Started


Alamofire는 iOS와 macOS에 대한 Swift 기반의 HTTP 네트워크 라이브러리입니다. 애플의 기초(Foundation) 네트워킹 스택의 맨 위에서 우아한 인테페이스를 제공하여 일반적인 네트워크 작업을 단순화합니다.

Alamofire는 연결가능한(chainable) 요청(request)/응답(response) 메소드, JSON 매개변수(parameter)와 응답 직렬화(response serialization), 인증(authentication) 및 기타 많은 기능들을 제공합니다.

이 Alamofire 튜토리얼에서 , 타사(third-party) RESTful API에서 파일 업로드하고 데이터 요청하는 것과 같은 기본적인 네트워킹 작업을 수행하기 위해 Alamofire를 사용할 것입니다.

Alamofie의 우아함은 Swift로 처음부터 작업되었고 Objective-C의 단짝인 AFNetworking에서 파생되지 않았습니다.

HTTP 네트워킹에 대한 개념적으로 이해하고 URLSession과 같은 Apple의 네트워킹 클래스에 대해 어느정도 알고 있어야 합니다.

Alamofire가 세부 구현을 모호하게 만드는 반면에, 네트워크 요청 문제를 해결해야 하는 경우 배경지식을 가지고 있는 것이 좋습니다.

시작하기(Getting Started)

예제 다운로드

주의
Alamofire는 일반적으로 CocoaPods을 사용하여 통합되었습니다. 다운로드한 프로젝트에 이미 설치되어 있습니다.

Alamofire 튜토리얼에 대한 앱의 이름은 PhotoTagger입니다. 완료될때, 이미지 라이브러리(실제 디바이스에서 실행하는 경우 카메라를 사용)에서 선택할 수 있게 해주고 Imagga라는 타사(third-party) 서비스로 이미지를 업로드할 것입니다. 이 서비스는 일부 이미지 인식을 수행하여 이미지에 대한 태그와 기본 색상 목록을 제공합니다.


이 프로젝트는 CocoaPods을 사용하며, PhotoTagger.xcworkspace파일을 사용하여 열어주세요.

주의
CocoaPods에 관해서 자세히 배우고 싶으면, Raywenderich 사이트에 등록된 Joshua Greene의 CocoaPods 튜토리얼 을 보세요.

프로젝트를 빌드하고 실행합니다. 다음처럼 보일것입니다.


Select Photo를 클릭하고 사진을 선택하세요. 배경 이미지가 여러분이 선택한 이미지로 바뀌게 될것입니다.

Main.storyboard를 열고 태그와 색상을 표시하기 위한 추가적인 화면이 추가되어있습니다. 남아있는것은 이미즈를 업로드하고 태그와 색상을 가져오는 것입니다.

Imagga API

Imagga는 이미지 위주의 클라우드 앱을 개발할 수 있도록 개발자와 비즈니스를 위해 이미지 태그 API를 제공하는 이미지 인식 플랫폼입니다. 여기에서 자동 태그 서비스의 데모를 볼수 있습니다.

Alamofire 튜토리얼을 위해 Imagga 무료 개발자 계정을 만들어야 합니다. Imagga는 각각의 HTTP 요청에 인증 헤더가 필요하기에 계정을 가진 사람만 서비스를 사용할 수 있습니다. https://imagga.com/auth/signup/hacker로 이동하고 양식을 작성하세요. 계정을 만들고 나서, 대쉬보드를 확인하세요.


인증(Authorization)섹션에 열거된(listed down) 것은 나중에 사용하게 될 비밀 토큰입니다. 이 정보를 모든 HTTP 요청 헤더에 포함해야 합니다.

주의
비밀토큰 전체를 복사했는지 확인하고, 오른쪽으로 스크롤해서 모든것을 복사했는지 확인하세요.

사진을 업로드하기 위해 Imagga의 content, 이미지 인식을 위해 tagging, 색상 식별을 위해 colors를 사용할 것입니다. http://docs.imagga.com/에서Imagga API에 관한 모든것을 읽을 수 있습니다.

REST, HTTP, JSON - 무엇인가요?(What’s that?)

인터넷으로 타사 서비스를 사용한 경험이 거의 없이 이 튜토리얼을 시작한 경우, 이러한 약어(acronyms)가 무엇을 의미하는지 궁금할 것입니다.

HTTP는 웹 서버가 화면에 데이터를 전송하기 위해 사용하는 앱 프로토콜 또는 규칙의 집합입니다. 여러분은 웹 브라우져에 입력한 모든 URL의 앞에 HTTP(또는 HTTPS)를 본적이 있습니다. FTP, Telnet, SSH와 같은 다른 앱 프로토콜을 들어본적이 있을것입니다. HTTP는 클라이언트(웹 브라우져나 앱)가 원하는 동작을 나타내는데 사용하는 몇가지 요청 메소드나 동사를 정의합니다.

  • GET : 웹 페이지 처럼 데이터를 검색하지만, 서버의 데이터는 변경하지 않습니다.
  • HEAD : GET과 동일하지만 헤더만 보내고 실제 데이터는 보내지 않습니다.
  • POST : 서버에 데이터를 보내며, 양식을 작성하고 제출(submit)하기를 클릭할때 일반적으로 사용됩니다.
  • PUT : 제공된 특정 위치로 데이터를 보냅니다.
  • DELETE : 제공된 특정 위치로부터 데이터를 삭제합니다.

REST 또는 REpresentational State Transfer은 일관된 설계에 대한 규칙 집합이며, 사용하기 쉽고 유지보수 가능한(maintainable) Web API 입니다. REST는 요청(request) 간에 상태를 지속하지 않으며, 요청 캐시 만들기, 균일한 인터페이스와 같이 몇가지 아키텍쳐 규칙이 있습니다. 요청에 대한 데이터 상테를 추적할 필요없이, 앱 개발자가 API를 쉽게 통합할 수 있습니다.

JSON은 JavaScript Object Notation의 약어입니다. 두 시스템간에 전송하는 데이터에 대해 사람이 읽을수 있고 이동하기 쉬운 메커니즘을 제공합니다. JSON은 데이터 타입을 제한합니다 : string, boolean, array, object/dictionary, nill, number. 정수와 소수간에 구별하지 않습니다.

메모리에서 객체를 JSON 또는 그 반대로 변환하기 위한 몇가지 기본 선택사항이 있습니다: 기존JSONSerialization 클래스와 새로 추가된 JSONEncoder와 JSONDecoder클래스가 있습니다. 게다가, JSON 처리에 도움이 되는 많은 타사(third part) 라이브러리가 있습니다. 이 튜토리얼에서는 그것들중 하나인, SwiftJSON을 사용할 것입니다.

HTTP, REST, JSON의 조합은 개발자로서 사용가능한 웹서비스의 많은 부분을 차지합니다. 어떻게 작은 부분이 대부분을 압도할 수 있는지 이해하려고 노력합니다. Alamofire와 같은 라이브러리는 이런 서비스 작업의 복잡성을 줄이는 데 도움이 될 수 있고, 그것들의 도움 없이 더 빨리 처리할 수 있습니다.

Alamofire는 무엇이 좋은가요?(What is Alamofire Good For?)

Alamofire가 왜 필요하나요? Apple은 이미 HTTP를 사용하여 컨텐츠 다운로드 하기 위해 URLSession과 다른 클래스들을 제공하고 있는데, 왜 타사(third party) 라이브러리를 사용하여 복잡하게 만드나요?

간단한 대답은 Alamofire는 URLSession을 기반으로 하지만, 네트워킹 코드를 훨씬 쉽게 만들어주어 상용구(boilerplate) 코드 작성하는것으로부터 해방시켜줍니다. 아주 적은 노력으로 인터넷 상의 데이터에 접근할 수 있고, 코드가 훨씬 명확하고 읽기 쉽습니다.

Alamofire에는 몇가지 중요한 함수가 있습니다.

  • Alamofire.upload : 멀티파트(multipart), 스트림(stream), 파일이나 데이터 메소드로 파일을 업로드 합니다.
  • Alamofire.download : 파일을 다운로드 하거나 이미 진행중인 다운로드를 재개합니다
  • Alamofire.request : 다른 모든 HTTP 요청은 파일 전송과 관련되어 있지 않습니다.

이러한 Alamofire 메소드들은 전역(global)으로 사용할수 있기에 클래스를 인스턴스화 할 필요가 없습니다. Alamofie의 근본이 되는 부분은 SessionManager, DataRequest, DataResponse 와 같은 클래스와 구조체 입니다. 하지만, 사용하기 위해서 Alamofire의 전체 구조 전부를 이해할 필요는 없습니다.

다음은 Apple의 URLSession과 Alamofire의 request 함수로 동일한 네트워크 작업을 하는 예제입니다.

// With URLSession
public func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) {
  guard let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true") else {
    completion(nil)
    return
  }

  var urlRequest = URLRequest(url: url,
                              cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
                              timeoutInterval: 10.0 * 1000)
  urlRequest.httpMethod = "GET"
  urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")

  let task = urlSession.dataTask(with: urlRequest)
  { (data, response, error) -> Void in
    guard error == nil else {
      print("Error while fetching remote rooms: \(String(describing: error)")
      completion(nil)
      return
    }

    guard let data = data,
      let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
        print("Nil data received from fetchAllRooms service")
        completion(nil)
        return
    }

    guard let rows = json?["rows"] as? [[String: Any]] else {
      print("Malformed data received from fetchAllRooms service")
      completion(nil)
      return
    }

    let rooms = rows.flatMap { roomDict in return RemoteRoom(jsonData: roomDict) }
    completion(rooms)
  }

  task.resume()
}

비교하기:

// With Alamofire
func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) {
  guard let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true") else {
    completion(nil)
    return
  }
  Alamofire.request(url,
                    method: .get,
                    parameters: ["include_docs": "true"])
  .validate()
  .responseJSON { response in
    guard response.result.isSuccess else {
      print("Error while fetching remote rooms: \(String(describing: response.result.error)")
      completion(nil)
      return
    }

    guard let value = response.result.value as? [String: Any],
      let rows = value["rows"] as? [[String: Any]] else {
        print("Malformed data received from fetchAllRooms service")
        completion(nil)
        return
    }

    let rooms = rows.flatMap { roomDict in return RemoteRoom(jsonData: roomDict) }
    completion(rooms)
  }
}

Alamofire에 필요한 설정이 더 짧고 함수가 무엇을 하는지 더 명확한 것을 볼수 있습니다. responseJSON(options:completionHandler:)로 응답을 비직렬화(deserialize) 하고 응답 상태코드가 200과 299 사이의 기본 허용범위에 있는지 확인하여 오류 조건 처리를 단순화 하는 응답상태 검증을 위해 validate()를 호출합니다.

이제 Alamofire를 직접 사용하면서 알아보도록 하겠습니다.


파일 업로드하기(Uploading Files)

ViewController.swift를 열고 맨 윗부분에서 import SwiftyJSON아래에 다음을 추가하세요.

import Alamofire

이를 통해서 코드에서 Alamofire 모듈이 제공하는 기능을 사용할수 있으며, 곧 사용할 것입니다.

다음으로, imagePickerController(_:didFinishPickingMediaWithInfo:) 끝부분으로 가서 dismiss(animated:)를 호출하기 전에 다음을 추가합니다.

// 1
takePictureButton.isHidden = true
progressView.progress = 0.0
progressView.isHidden = false
activityIndicatorView.startAnimating()

upload(image: image,
       progressCompletion: { [weak self] percent in
        // 2
        self?.progressView.setProgress(percent, animated: true)
  },
       completion: { [weak self] tags, colors in
        // 3
        self?.takePictureButton.isHidden = false
        self?.progressView.isHidden = true
        self?.activityIndicatorView.stopAnimating()
            
        self?.tags = tags
        self?.colors = colors
            
        // 4
        self?.performSegue(withIdentifier: "ShowResults", sender: self)
})

Alamofire는 모두 비동기(asynchronous)이며, 비동기 방식으로 UI를 업데이트 하는 것을 의미합니다.

  1. 업로드 버튼을 숨기고, 프로그래스 뷰와 activity 뷰를 보여줍니다.
  2. 파일을 업로드하는 동안, 업데이트된 퍼센트로 프로그래스를 처리합니다. 프로그래스바의 프로그래스 표시를 업데이트 합니다.
  3. 업로드가 완료될때 완료 처리를 실행합니다. 컨트롤들을 원래 상태로 되돌립니다.
  4. 마지막으로 업로드 성공 또는 실패가 완료될때 스토리보드가 결과 화면으로 이동합니다. 사용자 인터페이스는 오류 조건에 따라 변경되지 않습니다.

다음으로, 파일의 아랫부분에 있는 upload(image:progressCompletion:completion:)을 찾습니다. 이것은 현재 메소드 스텁(stub) 일뿐이기에, 다음과 같은 구현을 해줍니다.

func upload(image: UIImage,
            progressCompletion: @escaping (_ percent: Float) -> Void,
            completion: @escaping (_ tags: [String]?, _ colors: [PhotoColor]?) -> Void) {
  // 1
  guard let imageData = UIImageJPEGRepresentation(image, 0.5) else {
    print("Could not get JPEG representation of UIImage")
    return
  }

  // 2
  Alamofire.upload(multipartFormData: { multipartFormData in
    multipartFormData.append(imageData,
                             withName: "imagefile",
                             fileName: "image.jpg",
                             mimeType: "image/jpeg")
  },
                   to: "http://api.imagga.com/v1/content",
                   headers: ["Authorization": "Basic xxx"],
                   encodingCompletion: { encodingResult in
  })
}

여기에서 하는 일입니다.

  1. 업로드되는 이미지를 Data 인스턴스로 변환해줘야 합니다
  2. Imagga 컨텐츠 서버(endpoint)로 보내기 위해 JPEG 데이터 blob(imageData)을 MIME 멀티파트(multipart) 요청으로 변환합니다.

주의
Basic xxx부분을 Imagga 대시포드의 실제 인증 헤더로 교체해줘야 합니다.

다음으로, encodingCompletion 클로져를 추가하세요.

switch encodingResult {
case .success(let upload, _, _):
  upload.uploadProgress { progress in
    progressCompletion(Float(progress.fractionCompleted))
  }
  upload.validate()
  upload.responseJSON { response in
  }
case .failure(let encodingError):
  print(encodingError)
}

이 코드부분은 Alamofire upload함수를 호출하고 파일 업로드 처럼 프로그래스바를 업데이트 하기 위해 작은 계산을 해줍니다. 그리고나서 응답 상태코드가 200과 299 사이의 기본 허용범위에 있는지 확인합니다.

주의
Alamofire 4이전에는 프로그래스 콜백이 메인 큐(main queue)에서 호출되는 것을 보장하지 않았습니다. Alamofire 4부터는 새로운 프로그래스 콜백 API가 항상 메인 큐(main queue)에서 호출됩니다.

다음으로, upload.responseJSON 클로저에 대한 다음코드를 추가하세요

// 1
guard response.result.isSuccess,
  let value = response.result.value else {
    print("Error while uploading file: \(String(describing: response.result.error))")
    completion(nil, nil)
    return
}
                        
// 2
let firstFileID = JSON(value)["uploaded"][0]["id"].stringValue
print("Content uploaded with ID: \(firstFileID)")
                        
//3
completion(nil, nil)

다음은 위 코드에 대한 단계별로 설명한 것입니다.

  1. 업로드가 성공했는지 확인하고 결과값을 받습니다; 만약 그렇지 않는 경우 오류를 출력하고 완료 핸들러(completion handler)를 호출합니다.
  2. SwiftJSON을 사용하여, 응답으로부터 firstFileID를 가져옵니다
  3. UI를 업데이트하기 위해 완료 핸들러(completion handler)를 호출합니다. 이 시점에, 다운로드 태그나 색상이 없으므로, 단순히 데이터가 없음을 호출합니다.

주의
모든 응답(response)은 값과 타입으로 된Result열거형을 가집니다. 자동 유효성 검사를 사용하여, 가 200과 299 사이의 유효한 HTTP Code과 컨텐츠 타입(Content Type)이Accept HTTP 해더 필드에 지정된 유효한 타입을 반환할때 결과가 성공한 것으로 간주합니다.

다음과 같이 .validate 옵션을 추가하여 수동으로 유효성 검사를 수행할 수 있습니다.

Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])
  .validate(statusCode: 200..<300)
  .validate(contentType: ["application/json"])
  .response { response in
  // response handling code
}

다운로드 한 태그 나 색상이 없으므로 데이터가 없으면이 태그를 호출하십시오.

업로드하는 동안 오류가 발생하면 UI에 오류를 보여주지 않습니다: 사용자에게 태그나 색상을 반환하지 않습니다. 이것은 좋은 경험은 아니지만 이 튜토리얼에는 문제가 없습니다.

프로젝트를 빌드하고 실행합니다: 이미지를 선택하고 파일을 업로드와 프로그래스바가 변경되는 것을 지켜봅니다. 업로드가 완료되면 콘솔창에 다음과 같은 메모가 표시됩니다.


축하합니다. 웹상에서 파일 업로드가 성공하였습니다.


데이터 가져오기(Retriving Data)

Imagga에 이미지를 업로드하고 다음 단계는 Imagga가 사진을 분석한 후에 생성하는 태그를 가져오는 것입니다.

ViewController 확장의 upload(image:progress:completion:) 아래부분에 다음 메소드를 추가하세요.

func downloadTags(contentID: String, completion: @escaping ([String]?) -> Void) {
  // 1
  Alamofire.request("http://api.imagga.com/v1/tagging",
                    parameters: ["content": contentID],
                    headers: ["Authorization": "Basic xxx"])
     // 2
    .responseJSON { response in
      guard response.result.isSuccess,
        let value = response.result.value else {
          print("Error while fetching tags: \(String(describing: response.result.error))")
          completion(nil)
          return
      }
      
      // 3
      let tags = JSON(value)["results"][0]["tags"].array?.map { json in
        json["tag"].stringValue
      }
        
      // 4
      completion(tags)
  }
}

다음은 위 코드를 단계별로 설명한 것입니다.

  1. tagging 서버에 HTTP GET 요청을 수행하며, 업로드 후에 받은 ID로 URL 매개변수 컨텐츠를 보냅니다. 다시 말하지만, Basic xxx를 실제 인증받은 헤더로 교체해야 합니다.
  2. 응답이 성공했는지 확인하고, 결과 값을 가집니다: 그렇지 않으면 오류를 출력하고 완료 핸들러(completion handler)를 호출합니다.
  3. SwiftyJSON을 사용해서, 응답에서 tags 배열을 가져옵니다. tags 배열에서 각 딕셔너리 객체를 반복하고, tag 키와 관련있는 값을 가져옵니다
  4. 완료 핸들러(completiion handler)를 호출해서 서비스로부터 가져온 tags를 전달합니다.

다음으로 upload(image:progress:completion:)으로 돌아가고 성공 조건에 대한 완료 핸들러 호출을 다음처럼 교체합니다.

self.downloadTags(contentID: firstFileID) { tags in
  completion(tags, nil)
}

이렇게 하면 완료 핸들러에 태그를 보냅니다.

프로젝트를 빌드하고 실행합니다. 사진을 선택하고 다음과 비슷한 모습이 나타납니다.


괘 멋집니다! Imagga는 스마트한 API 입니다. :] 다음으로, 이미지의 색상을 검색해 볼것입니다.

ViewController 확장 downloadTags(contentID:completion:)아래에 다음에 오는 메소드를 추가하세요.

func downloadColors(contentID: String, completion: @escaping ([PhotoColor]?) -> Void) {
  // 1.
  Alamofire.request("http://api.imagga.com/v1/colors",
                    parameters: ["content": contentID],
                    headers: ["Authorization": "Basic xxx"])
    .responseJSON { response in
      // 2
      guard response.result.isSuccess,
        let value = response.result.value else {
          print("Error while fetching colors: \(String(describing: response.result.error))")
          completion(nil)
          return
      }
        
      // 3
      let photoColors = JSON(value)["results"][0]["info"]["image_colors"].array?.map { json in
        PhotoColor(red: json["r"].intValue,
                   green: json["g"].intValue,
                   blue: json["b"].intValue,
                   colorName: json["closest_palette_color"].stringValue)
      }
        
      // 4
      completion(photoColors)
  }
}

번호가 있는 주석에 대해 각각 이야기합니다

  1. colors서버에 HTTP GET 요청을 수행하며, 업로드하고 나서 가져온 ID를 URL 매개변수인 content로 보냅니다. 다시 말하지만, Basic xxx를 실제 인증받은 헤더로 교체해야 합니다.
  2. 응답이 성공했는지 확인하고, 결과 값을 가집니다: 그렇지 않으면 오류를 출력하고 완료 핸들러(completion handler)를 호출합니다.
  3. SwiftJSON을 사용하여 응답으로부터 image_colors 배열을 가져옵니다. image_colors에 있는 딕셔너리 객체를 각각 반복하고 PhotoColor객체로 변환합니다. 이 객체는 RGB 포멧과 문자열 색상이름 한쌍으로 만듭니다.
  4. 완료 핸들러(completiion handler)를 호출해서 서비스로부터 가져온 photoColors를 전달합니다.

마지막으로, upload(image:progress:completion)으로 돌아가고 성공 조건에 대한 완료 핸들러 호출을 다음처럼 교체합니다.

self.downloadTags(contentID: firstFileID) { tags in
  self.downloadColors(contentID: firstFileID) { colors in
    completion(tags, colors)
  }
}

이미지의 업로드 수행과 태그 다운로드, 색상 다운로드가 중첩됩니다.

프로젝트를 다시 빌드하고 실행합니다. 이번에, Colors버튼을 선택할때 색상 태그가 반환되는 것을 보게 됩니다.


뷰의 배경색상을 변경하기 위해 PhotoColor 구조체로 매핑된 RGB 색상을 사용합니다. 이제 Imagga에 이미지를 성공적으로 업로드하고 두개의 다른 서버에서 데이터를 가져왔습니다. 많은 일을 했지만(You’ve come a long way), PhotoTagger에서 Alamofire를 사용하는 방법을 개선할 여지가 있습니다.

PhotoTagger 개선하기(Improving PhotoTagger)

PhotoTagger에서 반복된 코드를 봤을것입니다. 만약 Imagga가 v2 API를 발표하고 v1을 폐기하면, PhotoTagger은 더이상 작동하지 않으므로, 3개의 메소드 각각에서 URL을 업데이트해야 합니다. 마찬가지로 인증 토큰이 변경되면 모든 곳에서 업데이트 해야합니다.

Alamofire은 중복 코드를 제거하고 중앙 집중식 구성을 제공하기 위해 간한단 메소들 제공합니다. 이 기술은 URLRequestConvertible을 준수하는 구조체를 만들고 업로드하거나 요청 호출을 업데이트하는 작업을 포함합니다.

File\New\File..를 클릭하여 새로운 Swift을 만들고 iOS아래의 Swift file을 선택합니다. Next를 클릭하고 파일 이름을 ImaggaRouter.swift로 하고, 노란색 폴더 아이콘인 PhotoTagger그룹을 선택하고 **Create를 클릭합니다.

새 파일에 다음을 추가하세요.

import Alamofire

public enum ImaggaRouter: URLRequestConvertible {
  // 1
  enum Constants {
    static let baseURLPath = "http://api.imagga.com/v1"
    static let authenticationToken = "Basic xxx"
  }
  
  // 2
  case content
  case tags(String)
  case colors(String)
  
  // 3
  var method: HTTPMethod {
    switch self {
    case .content:
      return .post
    case .tags, .colors:
      return .get
    }
  }
  
  // 4
  var path: String {
    switch self {
    case .content:
      return "/content"
    case .tags:
      return "/tagging"
    case .colors:
      return "/colors"
    }
  }
  
  // 5
  var parameters: [String: Any] {
    switch self {
    case .tags(let contentID):
      return ["content": contentID]
    case .colors(let contentID):
      return ["content": contentID, "extract_object_colors": 0]
    default:
      return [:]
    }
  }
  
  // 6
  public func asURLRequest() throws -> URLRequest {
    let url = try Constants.baseURLPath.asURL()
    
    var request = URLRequest(url: url.appendingPathComponent(path))
    request.httpMethod = method.rawValue
    request.setValue(Constants.authenticationToken, forHTTPHeaderField: "Authorization")
    request.timeoutInterval = TimeInterval(10 * 1000)
    
    return try URLEncoding.default.encode(request, with: parameters)
  }
}

다음은 위 코드를 단계별로 설명한 것입니다.

1.Imagga 기본 URL과 Basic xxx를 실제 인증받은 헤더를 상수선언합니다.
2. 열거형 case를 선언하며, 각 case는 API endpoint 에 해당합니다.
3. 각 api endpoint에 대한 HTTP method를 반환합니다.
4. 각 api endpoint에 대한 path를 반환합니다.
5. 각 api endpoint에 대한 매개변수(parameters)를 반환합니다.
6. 요청한 endpoint에 대해 URLRequest를 생성하기 위해 위의 모든 구성요소들을 사용합니다.

이제 모든 상용구(boilerplate) 코드가 한곳에 있으며, 업데이트가 필요할 때가 있습니다.

ViewController.swift로 돌아가서 upload(image:progress:completion:)을 교체합니다.

Alamofire.upload(
  multipartFormData: { multipartFormData in
    multipartFormData.append(imageData,
                             withName: "imagefile",
                             fileName: "image.jpg",
                             mimeType: "image/jpeg")
  },
  to: "http://api.imagga.com/v1/content",
  headers: ["Authorization": "Basic xxx"],

이부분을 다음으로 교체합니다.

Alamofire.upload(multipartFormData: { multipartFormData in
  multipartFormData.append(imageData,
                           withName: "imagefile",
                           fileName: "image.jpg",
                           mimeType: "image/jpeg")
},
  with: ImaggaRouter.content,

다음으로 downloadTags(contentID:completion:)에 있는 Alamofire.request에 대한 호출을 교체합니다.

Alamofire.request(ImaggaRouter.tags(contentID))

마지막으로, downloadColors(contentID:completion:)에 있는 Alamofire.request 호출을 업데이트 합니다.

Alamofire.request(ImaggaRouter.colors(contentID))

주의
이전에 편집한 두곳에서 responseJSON 핸들러는 그대로 두세요.

마지막으로 빌드하고 실행합니다 : 모든 기능이 이전과 같아야하며, 앱을 중단하지 않고 리팩토링햇음을 의미합니다. 하지만, Imagga 에서 변경된 사항이 있는 경우에 전체 소스코드를 검토해야 할 필요가 없습니다: API와 인증토큰, 매개변수 등등.. 정말 멋집니다!!

여기에서 어디로 가야하나요?(Where To Go From Here?)

이 튜토리얼은 매우 기본적인것을 다뤘습니다. https://github.com/Alamofire/Alamofire. 사이트에 있는 문저를 보면 더 깊이 알수 있습니다.

Alamofire가 사용하는 Apple의 URLSession에 대해 더 자세히 배우고 싶은 경우 약간의 시간이 필요합니다.


Posted by 까칠코더