활동 요약

  • 주간 TIL 정리
  • [Swift] 타입의 인스턴스 생성과 초기화 보장
  • 뷰 컨트롤러 사이의 데이터 전달
    • prepare(for:sender:) 메서드 이용
  • Segue 방식
    • 스토리보드
    • Custom Segue를 만들어 코드로 실행

활동 상세

[Swift] 타입의 인스턴스 생성과 초기화 보장

결론
Swift에 존재하는 타입의 인스턴스는 생성과 초기화가 이니셜라이저에 의해 보장되므로 테스트하지 않아도 된다. 반면 init?()과 같은 실패 가능한 이니셜라이저는 테스트 대상이다.

유닛 테스트를 학습하면서 어떤 것이 테스트의 대상이 되는지 고민했습니다. 예를 들어 타입의 인스턴스가 성공적으로 생성되었는지를 확인하는 것도 하나의 테스트라고 생각했었지요. 유닛 테스트 번들에서 아래와 같이 테스트해보았습니다.

// MARK: - Person class
class Person {
    let name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
}

// MARK: - Test method for initailizing person class
func testInitializingPerson_whenNameAndAgeAreGiven_hasGivenNameAndAge() {
    let ryan: Person = Person(name: "Ryan", age: 3)
    
    XCTAssertEqual(ryan.name, "Ryan")
    XCTAssertEqual(ryan.age, 3)
}

결과는 당연히 성공! 이때까지 저는 당연한 것은 없다고 생각하고 모든 것을 테스트해야 한다고 생각했습니다. 하지만 코드 리뷰를 받고 init?()과 같은 실패 가능한 이니셜라이저가 아니라면 이니셜라이저 자체를 테스트하지는 않아도 된다는 결론을 얻었어요.

리뷰 내용:
음.. 스위프트 인스턴스의 가장 큰 장점 중 하나는 init이 되면 인스턴스의 생성과 초기화가 보장된다는 것이에요. 그래서 아예 init에 대해서는 검증할 필요가 없습니다.
init? 등에 대해서는 검증할 필요가 있겠지만, init 자체에는 테스트 하지 않아도 됩니다.
Swift 언어 가이드의 Initialization 내용을 보면 왜 그런지 나와있을 것 같아요.

그러면 바로 찾아봐야죠!

Source: The Swift Programming Language (Swift 5.4) - Initialization

공식 문서 Initialization 문서에 있네요. 생성된 새로운 인스턴스가 최초에 사용되기 전에 올바르게 이니셜라이즈(초기화)하는 것이 이니셜라이저의 역할이라고 하고 있습니다.


[Swift] View Controller 이동 간 데이터 전달 방법

화면을 이동할 때 이전 화면에서 가지고 있던 데이터를 다음 화면으로 넘겨주어야 하는 경우가 있습니다. 이럴 때는 어떤 방법을 적용할 수 있을까요?

예시 상황

TableView에서 개별 Cell을 선택했을 때 상세 화면으로 전환되는 과정에서 데이터를 넘겨주는 상황을 예시로 들어보겠습니다.

TableView를 보여주는 과정에서 TableView를 담당한 ViewController가 이미 TableView와 다음 화면인 상세 화면에서 보여주어야할 정보를 모두 가지고 있습니다 (JSON 디코딩 데이터).

그렇다면 저희가 다음 ViewController에게 알려주어야 할 정보는 아래와 같습니다.
2. 선택된 CellindexPath.row에 따라 보여주어야 할 상세 화면의 데이터 (이름, 이미지, 상세 설명)

어떤 것을 다음 ViewController에 전달해야할지 알았으니 어떻게 하는지 알아볼까요?

prepare(for:sender:) 메서드 활용

UIViewControllerprepare(for:sender:) 메서드를 활용하면 다음 화면으로 정보를 넘겨줄 수 있습니다. 아래 이미지처럼 재정의되지 않은 perpare(for:sender:) 메서드는 아무 것도 하지 않습니다.

하지만 재정의하여 사용하면 전환될 화면인 다음 ViewController에 데이터를 넘겨줄 수 있죠. 코드를 보시겠습니다.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard let indexPath = tableView.indexPath(for: sender as! UITableViewCell) else {
        os_log(.fault, log: .ui, OSLogMessage.indexPathIsNil)
        return
    }
    
    if segue.identifier == Identifier.Segue.artworkDetail {
        let followingViewController = segue.destination as? ArtworkDetailViewController
        
        followingViewController?.artwork = artworks[indexPath.row]
    }
}

코드를 통해 목표한 요소를 넘겨주고 있음을 확인할 수 있습니다. 상세히 설명하면 아래와 같은 과정을 거치는 것이지요.
1. sender로부터 선택된 CellindexPath.row를 추출
2. artworks 배열의 indexPath.row 번째 인덱스에 있는 요소를 다음 ViewControllerartwork 프로퍼티로 할당

그럼 데이터를 받는 ViewController는 전달 받은 데이터를 저장할 프로퍼티를 가지고 있어야 합니다.

final class ArtworkDetailViewController: UIViewController {
    // MARK: - Properties
    var artwork: Artwork?
  
    (... 후략)
}

특정 Segue가 실행될 때 데이터를 전달하는 방법

아직 제가 재정의한 prepare(for:sender:) 메서드에서 설명되지 않은 부분이 있습니다. 코드를 계속해서 살펴보시죠.

if segue.identifier == Identifier.Segue.artworkDetail {
    let followingViewController = segue.destination as? ArtworkDetailViewController
    
    followingViewController?.artwork = artworks[indexPath.row]
}

위 조건문은 segue.identifier를 검사하여 Identifier.Segue.artworkDetail인 경우 코드 블럭 안의 내용을 실행한다고 명시하고 있습니다.

Segueidentifier를 설정하는 방법은 먼저 스토리보드에서 Segue를 만들고, Attributes InspectorStoryboard Segue 섹션의 Identifier를 작성해주시면 됩니다.

이상으로 UIViewControllerprepare(for:sender:) 메서드를 재정의하여 화면 전환 간 데이터를 전달하는 방법을 살펴보았습니다.


Segue를 실행하는 방법

기능들을 구현하는 과정에서 Segue를 수행하는 방법에 대해 크게 고민해보지 않았는데, 최근 코드를 통해 Segue를 실행하는 것을 보고 정리해두어야겠다고 생각했습니다.

1. 스토리보드 활용

보통 스토리보드를 활용해서 Segue를 수행하는 경우가 많죠. 아래 예시 이미지는 TableView에서 상세 화면으로 전환할 때 TableViewCellcontrol 키를 누르고 클릭한 후 드래그하여 다음 화면에서 놓는 모습입니다.

이 작업을 수행하면 아래와 같이 화면 전환이 일어난다는 Storyboard Segue 표시를 확인할 수 있습니다.

화면에서 강조된 요소 (예시에서는 TableView Cell)와 상호작용함으로써 다음 화면인 Artwork Detail View Controller로 전환됨을 알 수 있습니다.

2. 코드 활용

코드를 활용해 화면 전환을 하는 방법은 Custom Segue를 만들어서 코드로 작동시키는 방법이 있습니다. 먼저 아래와 같이 View Controller와 이동할 ViewController를 연결시켜 줍니다.

이후 만들어준 Storyboard Segueidentifier를 설정합니다.

애플리케이션 사용자의 활동에 따라 Segue가 일어나야 할 위치에 Segue가 발생하도록 performSegue(withIdentifier:sender:) 메서드를 작성해줍니다. 예시 상황은 TableView이므로 tableView(_:didSelectedRowAt:) 메서드에서 실행하도록 하겠습니다.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "showDetail", sender: indexPath.row)
}

1 번 방법의 경우 스토리보드에서 어떤 UI 요소와 상호작용하면 화면의 이동이 일어나는지 확인하기에 편리한 반면 코드 상에서는 확인하기 어렵고, 2 번 방법은 반대로 스토리보드에서 어떤 UI 요소와 상호작용하면 화면의 이동이 일어나는지 확인하기 어려운 반면 코드 상에서 확인하기 좋겠네요. 하지만 저는 시각적으로 잘 나타낼 수 있는 1 번 방법을 선호합니다!

이상 스토리보드와 코드를 활용한 Segue의 실행 방법을 알아보았습니다.

profile
합리적인 해법 찾기를 좋아합니다.

0개의 댓글