본문 바로가기

개발/iOS

[SwiftUI Essentials] Building Lists and Navigation - 1

지난 튜토리얼에서는 SwiftUI를 활용해서 여러 가지 View를 한 화면에 보이는 방법에 대해서 알아보았다.

이번 튜토리얼은 지난 번 만든 Landmark에서 나아가, 여러 다양한 랜드마크의 정보를 담고 있는 동적인 스크롤 리스트를 만드는 방법에 대해서 알아보는 튜토리얼이다. 또한, 서로 다른 크기를 가진 기기에 대응하는 방법에 대해서도 다룬다.

 

우선, 이번 포스팅에서는 랜드마크들의 정보가 저장된 json 파일로부터 데이터를 읽어, View에서 보이기 위한 사전 과정에 대해서 할 예정이다.

 

Landmark.swift

- 첫 번째 단계에서는 View에서 각 랜드마크의 정보에 대해서 보여줄 정보를 불러온다. 튜토리얼에서 제공하는 Resources 파일을 프로젝트 폴더 내로 복사한다. 이 때, 설정은 아래 그림과 같이 체크표시를 진행한다.

랜드마크 정보 불러오기

 

- 그리고 나서 데이터의 정보를 풀러올 Swift 파일(SwiftUI 파일이 아니다.)을 만들고, Landmark.swift 파일을 만들고 json 파일에 있는 정보들을 구조체 형식으로 선언할 것이다. 이 때 `Codable`을 통해 데이터 파일로부터 구조체를 불러오는 것을 훨씬 쉽게 할 수 있다고 튜토리얼에서는 소개하고 있다. 이 부분에 대해서는 추후에 다룬다.

- 다운로드 받은 파일에서 Resources 경로에 저장된 이미지들을 `Assets.xcassets` 폴더로 이동시킨다. 그 이후 Landmark 구조체에 이미지의 이름을 불러와 사용할 수 있도록 하는 구문을 추가한다. 이 때, 이미지 이름의 경우 `private` 속성을 주어 이미지 그 자체만을 다룰 수 있도록 설정한다.

- Landmark 구조체 내에 각 랜드마크의 위치 정보를 포함시키기 위한 코드를 추가한다. 위치 정보는 JSON 데이터 구조에 되어 따라 위도(latitude)와 경도(longitude)를 포함한 새로운 구조체 Coordinates를 선언하여 타입을 지정해준다.

- 저장된 위치 정보를 MapKit에서 사용하게 하기 위한 locationCoordinates를 선언한다.

 

이렇게 작성된 Landmark 구조체는 아래와 같다.

// Landmark.swift

import Foundation
import SwiftUI
import CoreLocation

struct Landmark: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    
    private var imageName: String
    var image: Image {
        Image(imageName)
    }
    
    struct Coordinates: Hashable, Codable {
        var latitude: Double
        var longitude: Double
    }

    private var coordinates: Coordinates
    var locationCoordinates: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: coordinates.latitude, longitude: coordinates.longitude
        )
    }
}

 

ModelData.swift 

 

- 랜드마크에 대한 정보를 저장할 구조체를 완성하였으니, 이제 랜드마크들의 정보가 저장된 landmarkData.json 파일을 읽어올 차례이다. 새로운 Swift 파일을 만들고 ModelData.swift라고 이름 짓는다. 이 코드에서는 json 파일을 읽어들여 배열 형태로 만드는 작업을 한다.

- load 함수를 만든다. load 함수에서는 파일이 번들에 포함되어 있는지, 데이터와 JSONDecoder가 유효한지 확인한 후 json 파일을 읽어 배열을 만든다. 이 때, 에러 처리하는 코드가 포함되어 있다. 각 경우에서 문제가 생길 경우 에러를 발생시킨다.

 

이렇게 작성된 코드는 아래와 같다.

 

// ModelData.swift

import Foundation

var landmarks: [Landmark] = load("landmarkData.json")

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else {
        fatalError("Couldn't find \(filename) in main bundle.")
    }
    
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
    
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

 

리팩토링

- 이제 랜드마크들의 정보를 읽어들여 사용할 준비가 완료되었다. 튜토리얼에서는 코드 구조를 리팩토링하는 과정이 들어간다. 이 부분은 튜토리얼의 방법을 따르던, 본인이 편한 방법을 사용할 수 있지만, 일단 튜토리얼이 수행한 대로 따르도록 하겠다.

- App에 포함되는 ContentView, CircleImage, MapView를 Views 폴더에, 데이터에 관련된 Landmark, ModelData를 Model 폴더에, json 데이터를 Resources 폴더에 넣어 경로를 정리한다.

 

이렇게 하면 아래와 같이 폴더 경로를 정리할 수 있다.

관련된 파일끼리 그룹을 만든다.


이제 파일로부터 데이터를 읽어들일 준비가 끝났다. 다음 포스팅에서는 받아들인 정보를 토대로 화면에 보이기 위해 기존 작성한 View들을 수정하는 작업에 대해서 포스팅 할 예정이다.

반응형