๐Ÿ“ฑ App Development Study/iOS๐ŸŽ

[Udemy iOS & Swift Bootcamp] H4XOR News(List, ObservedObject, WKWebView)

ibelieveinme 2023. 7. 8. 23:48
728x90

Algolia Search's API ๋ฅผ ์ด์šฉํ•ด์„œ Hacker News ์‚ฌ์ดํŠธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ iOS ์•ฑ์—์„œ ๋ฆฌ์ŠคํŠธ์™€ ์›น๋ทฐ๋กœ ๋ณด์—ฌ์ฃผ์ž.


[Hacker News ์‚ฌ์ดํŠธ]

https://news.ycombinator.com/

 

Hacker News

 

news.ycombinator.com

[Algolia Search's API]

https://hn.algolia.com/api

 

HN Search powered by Algolia

Hacker News Search, millions articles and comments at your fingertips.

hn.algolia.com


1. Project ์ƒ์„ฑ

H4XOR News ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ iOS ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์ž. SwiftUI ๋กœ ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค.

 

import SwiftUI

struct ContentView: View {
    var body: some View {
    	Text("Content")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

ContentVIew.swift ํŒŒ์ผ์˜ ๊ธฐ๋ณธ ์ฝ”๋“œ

 

 

2. List View, NavigationView ๋งŒ๋“ค๊ธฐ

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView{
            List {
                Text("Content")
                Text("Content")
                Text("Content")
            }.navigationTitle("H4XOR NEWS")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

List๋Š” ํฐ์ƒ‰ ์ค„ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋งŒ๋“ค์–ด์ง€๊ณ 

NavigationView๋Š” ๋ฆฌ์ŠคํŠธ ์œ„์— ๊ณต๊ฐ„์ด ์ƒ๊ธด๋‹ค. ์ด ๋•Œ navigationTitle์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๊ตต์€ ๊ธ€์”จ๋กœ ์ œ๋ชฉ์ฒ˜๋ฆฌ ๋œ๋‹ค.

 

 

3. Post ๊ฐ์ฒด ๋งŒ๋“ค๊ธฐ

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView{
            List {
                Text("Content")
                Text("Content")
                Text("Content")
            }.navigationTitle("H4XOR NEWS")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct Post: Identifiable{
    let id: String
    let title: String
}

let posts = [
    Post(id: "1", title: "Hello"),
    Post(id: "2", title: "Bonjour"),
    Post(id: "3", title: "Hola")
]

 

Post ๊ฐ์ฒด์— id์™€ title ์†์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

 

Struct, Class ๋ฅผ ์ •์˜ํ•  ๋•Œ ID ๊ฐ’์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, Identifiable ํ”„๋กœํ† ์ฝœ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ID ๊ฐ’์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ์ˆœ์„œ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

https://developer.apple.com/documentation/swift/identifiable

 

Identifiable | Apple Developer Documentation

A class of types whose instances hold the value of an entity with stable identity.

developer.apple.com

Identifiable ํ”„๋กœํ† ์ฝœ์—์„œ ID๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ id ๊ฐ’์„ ํ•„์ˆ˜๋กœ ์ •์˜ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์ฆ‰ let id: String ๊ตฌ๋ฌธ์ด ์—†์œผ๋ฉด ์—๋Ÿฌ๋‚œ๋‹ค.

 

 

4. List์— Identifiable ํ”„๋กœํ† ์ฝœ์„ ์ ์šฉํ•œ Post ๊ฐ์ฒด๋“ค ๋„์šฐ๊ธฐ

 

1) List ์ƒ์„ฑ ์œ ํ˜•์—์„œ Identifiable ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์„ ์–ธ ๋ฒ„์ „์„ ์„ ํƒํ•œ๋‹ค.

 

2) ์†์„ฑ ์ฑ„์šฐ๊ธฐ

์†์„ฑ์œผ๋กœ data: RandomAccessCollection ๊ณผ rowContent: (Identifiable) -> View ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

3) in ํ‚ค์›Œ๋“œ ์ด์šฉํ•ด์„œ ๋ฆฌ์ŠคํŠธ ๋ฐ˜๋ณต๋ฌธ ์„ค์ •ํ•˜๊ธฐ

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView{
            List(posts) { post in
                Text(post.title)
            }
            .navigationTitle("H4XOR NEWS")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct Post: Identifiable{
    let id: String
    let title: String
}

let posts = [
    Post(id: "1", title: "Hello"),
    Post(id: "2", title: "Bonjour"),
    Post(id: "3", title: "Hola")
]

 

collection data์—๋Š” ๋ฆฌ์ŠคํŠธ๋ช… posts ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ๊ณ , rowContent๋Š” ๊ด„ํ˜ธ๋ฅผ ๋‹ซ๊ณ  in ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ˜๋ณต๋ฌธ ํ˜•์‹์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

์‚ฌ์‹ค ์ด๋ ‡๊ฒŒ ๋ณ€ํ˜•ํ•˜๋Š” ๋ถ€๋ถ„์€ ์™„์ „ํžˆ ์™€๋‹ฟ์ง€๋Š” ์•Š์•„์„œ ๊ณ„์† ๋“ค์—ฌ๋‹ค๋ด์•ผ๊ฒ ๋‹ค.

 

 

5.  Algolia Search's API์˜ Json ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ 

https://hn.algolia.com/api

 

HN Search powered by Algolia

Hacker News Search, millions articles and comments at your fingertips.

hn.algolia.com

API Documentation์„ ๋ณด๋ฉด Json ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” URL ๋ฆฌ์ŠคํŠธ๋“ค์ด ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์šฐ์„  ์ฒซ ํŽ˜์ด์ง€์˜ ๋ชจ๋“  ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” http://hn.algolia.com/api/v1/search?tags=front_page URL์„ ์ด์šฉํ•ด๋ณด์ž.

 

https://hn.algolia.com/api/v1/search?tags=front_page ๊ฐ€ ์ „๋‹ฌํ•˜๋Š” Json ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋‹ˆ ํ•œ๋ฒˆ ๋ด๋‘์ž.

hits ๋ฆฌ์ŠคํŠธ๋“ค์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ๊ณ , hits ๋ฆฌ์ŠคํŠธ ํ•œ๊ฐœ๋Š” ์˜ค๋ฅธ์ชฝ๊ณผ ๊ฐ™์ด ์ƒ๊ฒผ๋‹ค. title, url, created_at, objectID, points ๋“ฑ์˜ ์ •๋ณด๊ฐ€ ์ฃผ์–ด์ง„๋‹ค.

 

 

6. Json ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ Models ํด๋ž˜์Šค๋“ค ๋งŒ๋“ค๊ธฐ

H4XOR ํด๋”์— Models ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ์•ˆ์— NetworkManager์™€ PostData swift ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

NetworkManager.swift ๋Š” API ํ˜ธ์ถœ ์ฝ”๋“œ๋ฅผ ๋‹ด๋‹นํ•˜๊ณ , PostData.swift ๋Š” Data ๊ฐ์ฒด๋ฅผ ๋‹ด๋‹นํ•  ๊ฒƒ์ด๋‹ค.

 

import Foundation

struct Results: Decodable {
    let hits: [Post]
}

struct Post: Decodable, Identifiable {
    var id: String{
        return objectID
    }
    let objectID: String
    let points: Int
    let title: String
    let url: String?
}

์œ„์—์„œ ContentView.swift ์— ์ •์˜ํ•œ Post ๊ตฌ์กฐ์ฒด๋ฅผ ์ง€์šฐ๊ณ  PostData.swift ํŒŒ์ผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์ •์˜ ํ•ด์ฃผ์ž.

๋˜ํ•œ Decodable ํ”„๋กœํ† ์ฝœ๋กœ Json ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๋กœ ๊ฐ์ฒด๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

 

hits ๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฆฌ์ŠคํŠธ์— Post ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๊ณ , Post ๋ฐ์ดํ„ฐ์—์„œ ์‚ฌ์šฉํ•  objectID, points, title, url ์„ ์„ ์–ธํ•ด์ค€๋‹ค.

์ด ๋•Œ, Identifiable ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด id ๋ฅผ ์ •์˜ํ•ด์ค˜์•ผํ•˜๋Š”๋ฐ, objectID ์™€ ๋‚ด์šฉ์ด ๊ฒน์น˜๋‹ˆ๊นŒ id ์†์„ฑ์„ objectID ๊ฐ’์œผ๋กœ ๋ฐ”๋กœ ๋„ฃ์–ด์ฃผ๋„๋ก ์œ„์™€ ๊ฐ™์€ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

 

* ๋’ค์—์„œ ์‹คํ–‰ํ•  ๋•Œ, url ์ด null ์ด๋ผ๋Š” ์—๋Ÿฌ๊ฐ€ ๋œจ๋ฉด let url: String? ์ด๋ ‡๊ฒŒ optional ์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ํ•ด๊ฒฐ๋œ๋‹ค.

import Foundation

class NetworkManager{
    func fetchData(){
        if let url = URL(string: "http://hn.algolia.com/api/v1/search?tags=front_page"){
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, response, error) in
                if error == nil{
                    let decoder = JSONDecoder()
                    if let safeData = data {
                        do{
                            let results = try decoder.decode(Results.self, from: safeData)
                        } catch{
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

NetworkManager.swift ์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™๋‹ค. url, session, task ๋ฅผ ์ •์˜ํ•˜๊ณ  json data๋ฅผ decoder๋กœ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ์ €์žฅํ•œ๋‹ค.

safeData๋ฅผ Results.self ๊ฐ์ฒด๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ.

 

 

7. JSON ๋ฐ์ดํ„ฐ ํ™”๋ฉด์— ๋ฟŒ๋ฆฌ๊ธฐ

1) NetworkManager ํด๋ž˜์Šค์— ObservableObject ํ”„๋กœํ† ์ฝœ ์ฑ„ํƒํ•˜๊ธฐ

import Foundation

class NetworkManager: ObservableObject{
    func fetchData(){
        if let url = URL(string: "http://hn.algolia.com/api/v1/search?tags=front_page"){
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, response, error) in
                if error == nil{
                    let decoder = JSONDecoder()
                    if let safeData = data {
                        do{
                            let results = try decoder.decode(Results.self, from: safeData)
                        } catch{
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

ํด๋ž˜์Šค์— ObservableObject ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋ฉด ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.

 

2) ๋ณ€ํ•˜๋Š” ๋ณ€์ˆ˜์— @Published ํ‚ค์›Œ๋“œ ๋ถ™์—ฌ์ฃผ๊ธฐ.

import Foundation

class NetworkManager: ObservableObject{
    
    @Published var posts = [Post]()
    
    func fetchData(){
        if let url = URL(string: "http://hn.algolia.com/api/v1/search?tags=front_page"){
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, response, error) in
                if error == nil{
                    let decoder = JSONDecoder()
                    if let safeData = data {
                        do{
                            let results = try decoder.decode(Results.self, from: safeData)
                            self.posts = results.hits
                        } catch{
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

posts ๋ฆฌ์ŠคํŠธ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ results.hits ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ฌ ๋•Œ๋งˆ๋‹ค post ๊ฐ์ฒด๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  @Published ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ฃผ๋ฉด ์ด ๋ณ€์ˆ˜๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๊ฐ์ฒด๋“ค์€ ์š” ๋ณ€์ˆ˜๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์†Œ์‹์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

 

https://developer.apple.com/documentation/combine/observableobject

 

ObservableObject | Apple Developer Documentation

A type of object with a publisher that emits before the object has changed.

developer.apple.com

https://developer.apple.com/documentation/combine/published

 

Published | Apple Developer Documentation

A type that publishes a property marked with an attribute.

developer.apple.com

 

3) ContentView.swift ์—์„œ posts ๊ฐ์ฒด ๊ตฌ๋…ํ•˜๊ธฐ

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var networkManager = NetworkManager()
    
    var body: some View {
        NavigationView{
            List(networkManager.posts) { post in
                Text(post.title)
            }
            .navigationTitle("H4XOR NEWS")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

//let posts = [
//    Post(id: "1", title: "Hello"),
//    Post(id: "2", title: "Bonjour"),
//    Post(id: "3", title: "Hola")
//]

@ObservedObjectํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์ธ๋‹ค. NetworkManager๊ฐ€ ์—…๋ฐ์ดํŠธํ•  ๋•Œ ๋งˆ๋‹ค ํŠธ๋ฆฌ๊ฑฐํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

List ์†์„ฑ์ธ data: RandomAccessCollection๋ฅผ posts -> networkManager.posts ๋กœ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.

 

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var networkManager = NetworkManager()
    
    var body: some View {
        NavigationView{
            List(networkManager.posts) { post in
                Text(post.title)
            }
            .navigationTitle("H4XOR NEWS")
        }
        .onAppear {
            self.networkManager.fetchData()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

.onAppear() ๋Š” body view ๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚ฌ์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

navigatio view ํ˜ธ์ถœ -> onAppear() ํ˜ธ์ถœ๋กœ ํ™”๋ฉด์— ์‹ค์งˆ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ์ด๋‹ค.

ํ์‡„๋œ ๊ณต๊ฐ„์ด๋ผ newtworkManager ์•ž์— self ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ค˜์•ผ ํ•œ๋‹ค.

 

๊ฒฐ๊ณผํ™”๋ฉด

 

4) ๋น„๋™๊ธฐ ์ ์šฉํ•˜๊ธฐ

import Foundation

class NetworkManager: ObservableObject{
    
    @Published var posts = [Post]()
    
    func fetchData(){
        if let url = URL(string: "http://hn.algolia.com/api/v1/search?tags=front_page"){
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { (data, response, error) in
                if error == nil{
                    let decoder = JSONDecoder()
                    if let safeData = data {
                        do {
                            let results = try decoder.decode(Results.self, from: safeData)
                            DispatchQueue.main.async {
                                self.posts = results.hits
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

์ฃผ์˜ํ•  ์ , @Published ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ํŒจ์น˜ํ•˜๋„๋ก ๋Š˜ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.

self.posts ์— ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋Š” ๋ถ€๋ถ„์— DisPatchQueue.main.async ์ฝ”๋“œ๋กœ ๊ฐ์‹ธ์ฃผ์ž.

 

5) ๊ฒŒ์‹œ๊ธ€์˜ ์ข‹์•„์š” ์ˆ˜๋„ ํ‘œ์‹œํ•˜๊ธฐ

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var networkManager = NetworkManager()
    
    var body: some View {
        NavigationView{
            List(networkManager.posts) { post in
                HStack{
                    Text(post.points)
                    Text(post.title)
                }
               
            }
            .navigationTitle("H4XOR NEWS")
        }
        .onAppear {
            self.networkManager.fetchData()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

8. ๋ชฉ๋ก์˜ ์„ธ๋ถ€์‚ฌํ•ญ ๋ทฐ ๋งŒ๋“ค๊ธฐ & ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

1) SwiftUI View ๋งŒ๋“ค๊ธฐ

Views ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ContentView๋ฅผ ๋„ฃ์–ด์ค€๋‹ค. SwiftUI View ํ…œํ”Œ๋ฆฟ์„ ์„ ํƒํ•˜์—ฌ DetailView๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

 

2) ContentView ์— NavigationLink ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

๋ฆฌ์ŠคํŠธ์˜ ์„ธ๋ถ€์‚ฌํ•ญ ํŽ˜์ด์ง€๋Š” NavigationLink ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

 

NavigationLink ์„ ์–ธ ๋ชฉ๋ก ์ค‘์—์„œ destination ๊ณผ label ์„ ์ธ์ž๋กœ ๊ฐ–๋Š” ์„ ์–ธ์„ ์„ ํƒํ•œ๋‹ค. destination view๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” navigation link ๋ฅผ ๋งŒ๋“œ๋Š” ๊ตฌ๋ฌธ์ด๋‹ค. destination์— ๋ชฉ์ ์ง€ ๋ทฐ ์ด๋ฆ„์„ ์ ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

 

https://developer.apple.com/documentation/swiftui/navigationlink#Link-to-a-destination-view

 

NavigationLink | Apple Developer Documentation

A view that controls a navigation presentation.

developer.apple.com

https://developer.apple.com/documentation/swiftui/navigationlink/init(destination:label:)-27n7s 

 

init(destination:label:) | Apple Developer Documentation

Creates a navigation link that presents the destination view.

developer.apple.com

 

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var networkManager = NetworkManager()
    
    var body: some View {
        NavigationView{
            List(networkManager.posts) { post in
                NavigationLink (
                    destination: DetailView(url: post.url)){
                        HStack {
                            Text(String(post.points))
                            Text(post.title)
                        }
                    }
                }
                .navigationTitle("H4XOR NEWS")
        }
        .onAppear {
            self.networkManager.fetchData()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

์ „์ฒด ์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™๋‹ค.

 

NavigationLink ( destination: DetailView(url: post.url)) { } ์ด๋ ‡๊ฒŒ  url ์„ DetailView ์ธ์ž๋กœ ๋„ฃ์–ด์„œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ,

์ด๋ ‡๊ฒŒ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฑฐ ๊ธฐ์–ตํ•˜์ž. label ์ธ์ž๋ฅผ ์•ˆ์“ฐ๊ณ  {} ๋กœ ๋๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋“ฏ.

 

๋ฉ”์ธ ํ™”๋ฉด์— Navigation Link ๊ฐ€ ์ƒ๊ฒผ๊ณ , ๋ˆ„๋ฅด๋‹ˆ Detail View ๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

 

9. Detail View ๋ฅผ ์›น๋ทฐ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ

1) WebView ๊ตฌ์กฐ ๋งŒ๋“ค๊ธฐ

import SwiftUI
import WebKit

struct DetailView: View {
    
    let url: String?
    
    var body: some View {
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(url: "https://www.google.com")
    }
}

struct WebView : UIViewRepresentable {
    func makeUIView(context: Context) -> some UIView {
        <#code#>
    }
    func updateUIView(_ uiView: UIViewType, context: Context) {
        <#code#>
    }
}

WebKit๋ฅผ import ํ•ด์ฃผ๊ณ  UIViewRepresentable ํ”„๋กœํ† ์ฝœ์„ ์ƒ์†๋ฐ›์€ WebView ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

UIViewRepresentable์€ UIKit view๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” SwiftUIView๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ์ด๋‹ค.

 

UIViewRepresentable ํ”„๋กœํ† ์ฝœ์˜ ํ•„์ˆ˜ Delegate ํ•จ์ˆ˜๋Š” makeUIView(context: Context)์™€ updateUIView(_ uiView: UIViewType, context: Context) ์ด๋‹ค.

 

makeUIView ๋Š” View ๋ฅผ WebView๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.

updateUIView ๋Š” View ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

 

2) DetailView ์˜ body ์—์„œ WebView์— url ์„ ์ „๋‹ฌํ•œ๋‹ค.

import SwiftUI

struct DetailView: View {
    
    let url: String?
    
    var body: some View {
        WebView(urlString: url)
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(url: "https://www.google.com")
    }
}

 

3) DetailView๊ฐ€ ์ค€ url ์„ urlString ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์„œ ํ™”๋ฉด์— ๋ฟŒ๋ ค์ค€๋‹ค.

struct WebView : UIViewRepresentable {
    let urlString: String?
    
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        if let safeString = urlString{
            if let url = URL(string: safeString){
                let request = URLRequest(url: url)
                uiView.load(request)
            }
        }
    }
}

 

urlString ์ด ์žˆ์œผ๋ฉด URLRequst๋ฅผ ํ†ตํ•ด ํ™”๋ฉด์„ load ํ•ด์ค€๋‹ค.

UIViewType ์€ UIWebView ๋Œ€์‹  WKWebView ๋ฅผ ์‚ฌ์šฉํ•ด์ฃผ์ž. ์ƒˆ๋กœ ๋‚˜์˜จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ๋„ ํ•˜๊ณ  ์›น ํŽ˜์ด์ง€์—์„œ ํ• ๋‹น๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์•ฑ๊ณผ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๊ด€๋ฆฌํ•˜์—ฌ ์›น ํŽ˜์ด์ง€์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ปค๋„ ์•ฑ์ด ์ฃฝ์ง€ ์•Š๋Š”๋‹ค.

 

4) ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง: ์ฝ”๋“œ์˜ ๋…๋ฆฝ์„ฑ๊ณผ ์œ ์—ฐ์„ฑ์„ ์œ„ํ•ด WebView.swift ํŒŒ์ผ๋กœ ๋นผ์ฃผ๊ธฐ.

import Foundation
import WebKit
import SwiftUI

struct WebView : UIViewRepresentable {
    let urlString: String?
    
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        if let safeString = urlString{
            if let url = URL(string: safeString){
                let request = URLRequest(url: url)
                uiView.load(request)
            }
        }
    }
}

 

5) ๊ฒฐ๊ณผํ™”๋ฉด. ๋—.

 

728x90