xcode 좌측 창을 네비게이션판넬이라고하는데, 거기에 import 할 파일을 원하는 경로에 드래그&드랍 한다.
그럼 창이 팝업 되는데, Copy items if needed
라는 옵션에 체크하고 finish를 눌러 import 한다.
이 옵션을 체크하면 복사본을 만들어서 import 하게 된다.(프로젝트 내에 파일이 없다면)
만약 체크하지 않았는다면,
원래 있던 경로를 참조하게 되어 프로젝트 종료시 또는 git을 통해서 올렸을때 다른사람이 pull 을 받으면 파일이 없을 수 있음
그래서 되도록이면 체크하고 사용하는것이 일반적이라고 함
Swift File 을 하나 만든다 (커맨드+n -> Swift File 클릭 -> 이름 지정 및 생성)
구조체를 선언한다
import Foundation
struct Landmark: Hashable, Codable {
}
json에서 읽어올 키를 만들어준다 (Hashable, Codable 타입은 따로 공부해야겠다)
import Foundation
struct Landmark: Hashable, Codable {
var id: Int
var name: String
var park: String
var state: String
var description: String
}
외부에서 몰라도 되는 값은 private로 선언
import Foundation
import SwiftUI
struct Landmark: Hashable, Codable {
var id: Int
var name: String
var park: String
var state: String
var description: String
// 외부에서 이미지 이름을 사용하지 않고, 이미지를 바로 사용할 때,
private var imageName: String // imageName값은 외부에서 접근할게 아니기 때문에 private
var image: Image { // 이미지 이니셜라이저로 생성한걸 가져다 쓸 예정
Image(imageName)
}
}
Swift File 을 만든다
로드 함수를 추가한다(swift는 json 파일을 읽는 함수를 직접 만들어야 된다고 한다. 그래서 대부분 Alamofire 라는 라이브러리를 사용해서 구현한다고 하니 나중에 사용법을 알아봐야 겠다.
tutorial 에서 제공하는 load 함수
import Foundation
var landmarks: [Landmark] = load("landmarkData.json") // 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)")
}
}
파일들은 Views
, Model
, Resources
로 그룹화
swift 파일에 var로 선언하면 다른 파일에서도 사용이 가능하다
// ModelData.swift
import Foundation
var landmarks: [Landmark] = load("landmarkData.json") // 이 값을 다른 파일에서 그냥 사용하니까 사용이 됨
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
// --생략--
// LandmarkRow.swift
// --생략--
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarks[0]) // 여기서 그냥 이렇게 가져다 쓸수 있음
}
}
struct 로 선언한 구조체를 타입처럼 선언해서 사용
// LandmarkRow.swift
import SwiftUI
struct LandmarkRow: View {
var landmark: Landmark // Landmark 구조체를 타입처럼 써줌
// --생략--
struct 로 선언한 구조체를 다른곳에서 사용할 때 구조체 안에 선언한 변수를 매개변수로 사용할 수 있음
// LandmarkRow.swift
import SwiftUI
struct LandmarkRow: View {
var landmark: Landmark // 이 변수를 저기 아래에서 매개변수 지정한거 처럼 사용
var body: some View {
HStack {
landmark.image
.resizable()
.frame(width: 50, height:50)
Text(landmark.name)
Spacer()
}
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarks[0]) // 구조체를 불러서 인자에 값을 넣어주는 방식으로 사용
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group{
LandmarkRow(landmark: landmarks[0])
LandmarkRow(landmark: landmarks[1])
}
.previewLayout(.fixed(width: 300, height: 70)) // 이 메소드를 이용해서 크기를 지정해서 볼 수 있음
}
}
List
로 묶어 주면 됨// LandmarkList.swift
struct LandmarkList: View {
var body: some View {
List { // List 로 묶어주면 여러개 띄우기 가능
LandmarkRow(landmark: landmarks[0]) // import없이 그냥 가져다가 사용 가능
LandmarkRow(landmark: landmarks[1])
}
}
}
struct LandmarkList: View {
var body: some View {
List(landmarks, id: \.id) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
Identifiable
프로토콜을 이용하면 id 속성을 가져다 쓸 수 있음
Landmark.swift
import Foundation
import SwiftUI
import CoreLocation
struct Landmark: Hashable, Codable, Identifiable {
var id: Int // Identifiable 프로토콜을 지정 하기 위해서는 id속성이 있어야 됨
var name: String
var park: String
var state: String
var description: String
// --생략--
LandmarkList.swift
struct LandmarkList: View {
var body: some View {
List(landmarks) { landmark in // 이제 이렇게 쓸수 있음
LandmarkRow(landmark: landmark)
}
}
}
NavigationView
로 감싼다struct LandmarkList: View {
var body: some View {
NavigationView { // 리스트에 네이게이션 기능을 넣어줄 예정
List(landmarks) { landmark in
LandmarkRow(landmark: landmark)
}
.navigationTitle("Landmarks") // 이렇게 하면 리스트에 navigation 제목을 달아줄 수 있음(이건 navigation 된 후에 되돌아가는 버튼에 보이는 글씨이기도 함)
}
}
}
NavigationLink
를 넣어서 이동할 View를 정해주고, label
을 지정해서 누를 버튼을 랜더링struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarks) { landmark in
NavigationLink { // 여기 안에 있는 페이지(디테일뷰)로 이동
LandmarkDetail()
} label: { // 여기 안에 있는 View를 클릭해서 이동
LandmarkRow(landmark: landmark)
}
}
.navigationTitle("Landmarks")
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) // 이 줄을 추가해서 원하는 디바이스 미리보기를 활성화
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
ForEach(["iPhone SE (2nd generation)", "iPhone XS Max"], id: \.self) { deviceName in
LandmarkList()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName) // 이 줄을 추가해주면 미리보기 상단에 디바이스명 표시
}
}
}
튜토리얼 2단계를 하면서 궁금한 점,
swift 파일 내에서 var로 선언하면 다른 파일에서도 사용이 가능한 전역 변수가 되는지?
(ModelData 파일에서 변수에 담아준 json로드값을 다른곳에서 그냥 사용함;;)
struct 로 구조체를 생성하면 타입으로 사용할 수 있는건지?
(Landmark 파일에 선언한 구조체를 다른곳에서 타입으로 지정해서 사용하는 코드가 있음;;)