다음 학기에 프로젝트를 하게됐다. 팀 프로젝트가 처음은 아니지만, 이전에서는 어쩔 수 없이 백엔드를 담당해서 아쉬움이 컸다. 그래서 종강 하기전부터 프로젝트를 찾아다녔고 운 좋게도 프로젝트에 들어가 이것저것 준비하고 있다. 거창한 준비는 아니지만, 실제 프로젝트에 들어가기 전에 어떤 기술이 필요할지 고민해보고 그걸 미리 연습해보고 있다.
그 중 하나가 HTTP 통신이다. 혼자서 트위터 클론코딩을 했을 때는 파이어베이스를 이용했지만 앞으로 팀원이 구축한 서버와 통신을 이용해야하니 이 부분을 공부 중이다. 이전에 Swift의 오픈소스들을 찾아봤었고 KingFisher와 Alamofire가 유명한 것을 알고있었기 때문에 바로 Alamofire를 실습해보았다.
필수적인 부분을 요약해보면
1. Codable 프로토콜을 따르는(Conform) Struct 만들기
2. ViewModel에서 사용하기
이렇게 정리할 수 있을것 같다.
연습을 위해서 jsonplaceholder 사이트를 이용했고 Add Package 과정은 생략한다.
애플 개발 공식문서 표현을 빌리자면,
structure that conforms to the Codable protocol 을 만들어줘야 JSON 형식을 디코딩, 인코딩 할 수 있다.
디코딩은 외부 표현에서 내부 모델로 변환하는 것이고, 인코딩은 그 반대다. 외부 표현은 여러 형식이 될 수 있는데 대부분 JSON일 것이다.
디코딩을 위해서는 Decodable, 인코딩을 위해서는 Encodable 프로토콜을 이용해야하고 이 둘을 묶어 놓은 것이 Codable 프로토콜이다. 편하게 Codable만 conform 해준다.
위는 jsonplaceholder 사이트의 todos json 형식이다. 이걸 struct로 나타내보자.
import Foundation
struct Todo: Identifiable, Codable {
var userId: Int
var id: Int
var title: String
var completed: Bool
}
Codable과 Identifialbe을 confrom했다. 데이터를 받아와서 나중에 List에서 결과를 보여줄 것이기 때문에 Identifiable을 추가했다.
JSON의 Key타입과 그 형식을 그대로 따라서 만들면 된다. todos에서는 단일의 Todo가 아닌 Todo 배열을 GET하는데 이건 나중에 ViewModel에서 처리할 것이다.
다른 예시를 보자.
내부에 중첩으로 되어있는 구조다. 밖에있는 것부터 하나씩 struct를 만들어가면 된다.
import Foundation
struct CustomerResponse: Codable {
var customer: Customer
}
struct Customer: Codable, Identifiable {
var id: String
var name: String
var phone: String
var address: Address
}
struct Address: Codable {
var street: String
var city: String
var state: String
}
생성하는 모든 struct는 Codable를 confrom하고 List에 표현될 정보에만 identifiable을 덧붙이면 된다.
json의 key와 변수의 이름을 다르게 사용하고 싶을 때는 Codingkey를 conform 하는 enum 타입을 만들어준다.
import Foundation
struct Todo: Identifiable, Codable {
var userId: Int
var id: Int
var header: String
var completed: Bool
private enum CodingKeys: String, CodingKey {
case userId = "userId"
case id = "id"
case header = "title"
case completed = "completed"
}
}
jsonplaceholder는 title이라는 key를 가지고 있는데 struct에서는 header라고 작성했다. 이런 경우 enum 타입에서 header의 value에 기존의 key를 넣어주면 된다.
import Foundation
import Alamofire
class TodoViewModel: ObservableObject {
@Published var todo = [Todo]()
init() {
fetchTodos()
}
func fetchTodos() {
let url = "https://jsonplaceholder.typicode.com/todos"
AF.request(url, method: .get)
.responseDecodable(of: [Todo].self) { data in
guard let data = data.value else { return }
self.todo = data
}
}
func postPost() {
let url = "https://jsonplaceholder.typicode.com/posts"
let param: [String: Any] = [
"userId" : 1000,
"id" : 1000,
"title" : "title",
"body" : "body"
]
AF.request(url, method: .post, parameters: param, encoding: JSONEncoding.default)
.responseDecodable(of: Post.self) { response in
print("POST DEBUG : \(response)")
}
}
}
Alamofire를 import하고 AF.request를 이용해 동작시킬 수 있다.
url은 필수적으로 입력해야하고 method에서 post, get, delete, put 등등을 선택할 수 있는데 디폴트 값은 get이다.
예제들은 찾아보면 reponseJson을 이용하고 있는데 deprecated되어 responseDecodable을 이용하라고 한다. 크게 차이점은 없고 of에 어떤 타입인지 넣어주면 된다.
fetchTodos()에서는 json type이 배열로 들어오기때문에 [Todo].self로 만들었고 위에서 작성한 코드는 아니지만 postPost()에서는 post하려는 형식에 맞는 Post struct를 만들어 올려주고 있다.
Todos를 정상적으로 가져온 결과와
POST를 정상적으로 한 결과다.
https://haningya.tistory.com/m/33
https://www.youtube.com/watch?v=aMes-DVVJg4&t=1112s