Swift Xib 활용+Rx [1]

마이노·2023년 4월 26일
0
post-thumbnail

이번 동물의숲 앱을 만들어보면서, 참 많은 내용을 배워가는 것 같습니다.
기존에 SwiftUI로 만들었지만 UIKit이 오랜만에 땡기더라구요. 이제부터 동물의숲은 UIKit으로 만들기로 했습니다 짜잔~~

이번 포스팅에서는 뷰를 어느정도 xib로 만들어보고, (SnapKit 한스푼으로) 뷰를 그려보겠습니다.

어떤 것을 만들어볼지 생각해보기

테이블 뷰를 그리기 위해서 어떤 것들이 필요할까요?
1. 첫번째로 테이블 뷰 자체가 있을 것 입니다.
2. 두번째는 Cell이 필요하겠구요.
3. 추가로 저는 헤더까지 해보겠습니다.

1. 파일 만들기

각각 새 파일을 생성하고 타입을 알맞게 지정해줍시다.

class FishView: UIView {}
class FishViewCell: UITableCell {}
class FishViewHeader: UITableViewHeaderFooterView {}

편의상 똑같은 이름의 파일을 생성해주겠습니다. View 파일을 추가해줍시다.

방금 만든 클래스와 연결을 시켜주시면 기초단계는 끝입니다.

이후에는 평소 스토리보드를 쓰는 것 처럼 레이아웃잡고 테이블뷰 올리고 IBOulet연결해주시면 끝입니다. Cell과 Header또한 같은 방법으로 만들어주시면 되겠습니다.

2. 뷰 컨트롤러로 연결하기

뷰 컨트롤러를 만들어서 방금만든 파일을 연결을 해봅시다.

typealias Header = FishViewHeader
typealias Item = FishViewCell
    
lazy var fishView: FishView = {
	let v: FishView = .fromNib()
	v.tableView.register(Item.nib, forCellReuseIdentifier: Item.reuseIdentifier)
	v.tableView.register(Header.nib, forHeaderFooterViewReuseIdentifier: Header.reuseIdentifier)
	return v
    }()

cell과 header를 사용하기 위해 등록하는 과정에서 nib과 resueIdentifier가 필요합니다.

확장성을 위해 그리고 편의를 위해 저는 다음과 같은 Extension을 만들었습니다.

extension UIView {
	static var nibName: String { String(describing: Self.self) }
    static var nib: UINib { UINib(nibName: nibName, bundle: nil) }
    
    static func fromNib<T: UIView>() -> T {
    return Bundle(for: T.self).loadNibNamed(String(describing: T.self),
    owner: nil,
    options: nil)![0] as! T
    }
}

extension UITableViewCell {
    static var reuseIdentifier: String {String(describing: Self.self)}
}

extension UITableViewHeaderFooterView {
    static var reuseIdentifier: String { String(describing: Self.self) }
}

테이블뷰 셀을 만들어 사용하다보면 셀의 Identifier를 하드코딩해서 사용하는 경우를 보셨나요? 이러한 실수를 줄이기 위한 방법입니다. 출력해보면 다음과 같은 결과를 얻을 수 있습니다.

이후 코드로 레이아웃을 잡아주면 뷰 구성은 끝입니다. 뷰컨에 올릴 뷰만 레이아웃을 잡아주면되겠죠?

view.addSubview(fishView)
...
fishView.snp.makeConstraints { make in
	make.top.equalTo(view.safeAreaLayoutGuide)
	make.left.bottom.right.equalToSuperview()
}

3.서버로 데이터 받기

기존의 Rx를 이용하지 않고 테이블뷰를 구성해야 한다면 생각보다 귀찮은 일이 됩니다.
먼저 데이터를 받을 Subject를 하나 만들어줍니다.

서버로부터 데이터를 해당변수로 넘겨주는 것까지가 API의 목표입니다.

let myData = BehaviorRelay<[Fish]>(value: [])

func getFish() async throws {
	print(#function)
	let url = URL(string: AddressConstants.url + parameter)
	let version: String = AddressConstants.version
	let myKey = Bundle.main.apiKey
	var request = URLRequest(url: url!)
	request.setValue(myKey, forHTTPHeaderField: "X-API-KEY")
	request.setValue(version, forHTTPHeaderField: "Accept-	version")
        
	let (data, _) = try await URLSession.shared.data(for: request)
	let result = try JSONDecoder().decode([Fish].self, from: data)

	myData.accept(result)
}

BehaviorRelay는 초깃값을 가질 수 있습니다. 서버로부터 데이터를 받기 전 아무것도 없으니 일단 빈 배열을 가지고 있다가 값이 없을 때 progress와 같은 화면구성을 추가 할 수 있겠습니다. 또한 values값을 변경할 수 있고 최신값을 받을 수 있습니다. 무엇보다 중요한 것은 completed, .error를 발생시키지 않고 Dispose되기 전까지 작동하기 때문에 UI Event에 사용하기에 적절한 Subject Wrapper입니다. 그래서 이름이 accept입니다..! 값을 받아드리기만 하기 때문이죠. (아무튼 좋다는뜻 🤩)
자.. 데이터를 받는 함수도 만들어주었습니다.

글이 길어져 다음 포스팅에는 viewDidLoad에 돌아와 데이터를 받는 함수를 실행하고 테이블뷰와 Rx를 연결해보겠습니다.

profile
아요쓰 정벅하기🐥

0개의 댓글