News API : https://newsapi.org/
ContentView.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | // // ContentView.swift // SwiftUIProject // // Created by Cairocoders // import SwiftUI struct ContentView: View { var body: some View { NewsFeedView() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } struct NewsFeedView: View { @ObservedObject var newsFeed = NewsFeed() var body: some View { NavigationView { List(newsFeed) { (article: NewsListItem) in NavigationLink(destination: NewsListItemView(article: article)) { NewsListItemListView(article: article) .onAppear { self.newsFeed.loadMoreArticles(currentItem: article) } } } .navigationBarTitle( "Apple News" ) } } } struct NewsListItemView: View { var article: NewsListItem var body: some View { VStack { UrlWebView(urlToDisplay: URL(string: article.url)!) .edgesIgnoringSafeArea(.all) .navigationBarTitle(article.title) } } } struct NewsListItemListView: View { var article: NewsListItem var body: some View { HStack { VStack(alignment: .leading) { Text( "\(article.title)" ) .font(.headline) Text( "\(article.author ?? " No Author ")" ) .font(.subheadline) } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | // // NewsFeedModels.swift // SwiftUIProject // // Created by Cairocoders // import Foundation class NewsFeed: ObservableObject, RandomAccessCollection { typealias Element = NewsListItem @Published var newsListItems = [NewsListItem]() var startIndex: Int { newsListItems.startIndex } var endIndex: Int { newsListItems.endIndex } var loadStatus = LoadStatus.ready(nextPage: 1) var urlBase = "https://newsapi.org/v2/everything?q=apple&sortBy=popularity&apiKey=55c250f06e1144c29a3ec4d2530adbe5&language=en&page=" init() { loadMoreArticles() } subscript(position: Int) -> NewsListItem { return newsListItems[position] } func loadMoreArticles(currentItem: NewsListItem? = nil) { if !shouldLoadMoreData(currentItem: currentItem) { return } guard case let .ready(page) = loadStatus else { return } loadStatus = .loading(page: page) let urlString = "\(urlBase)\(page)" let url = URL(string: urlString)! let task = URLSession.shared.dataTask(with: url, completionHandler: parseArticlesFromResponse(data:response:error:)) task.resume() } func shouldLoadMoreData(currentItem: NewsListItem? = nil) -> Bool { guard let currentItem = currentItem else { return true } for n in (newsListItems.count - 4)...(newsListItems.count-1) { if n >= 0 && currentItem. uuid == newsListItems[n]. uuid { return true } } return false } func parseArticlesFromResponse(data: Data?, response: URLResponse?, error: Error?) { guard error == nil else { print( "Error: \(error!)" ) loadStatus = .parseError return } guard let data = data else { print( "No data found" ) loadStatus = .parseError return } let newArticles = parseArticlesFromData(data: data) DispatchQueue.main.async { self.newsListItems.append(contentsOf: newArticles) if newArticles.count == 0 { self.loadStatus = .done } else { guard case let .loading(page) = self.loadStatus else { fatalError( "loadSatus is in a bad state" ) } self.loadStatus = .ready(nextPage: page + 1) } } } func parseArticlesFromData(data: Data) -> [NewsListItem] { var response: NewsApiResponse do { response = try JSONDecoder().decode(NewsApiResponse.self, from: data) } catch { print( "Error parsing the JSON: \(error)" ) return [] } if response.status != "ok" { print( "Status is not ok: \(response.status)" ) return [] } return response.articles ?? [] } enum LoadStatus { case ready (nextPage: Int) case loading (page: Int) case parseError case done } } class NewsApiResponse: Codable { var status: String var articles: [NewsListItem]? } class NewsListItem: Identifiable, Codable { var uuid = UUID() var author: String? var title: String var url: String enum CodingKeys: String, CodingKey { case author, title, url } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // // UrlWebView.swift // SwiftUIProject // // Created by Cairocoders // import SwiftUI import WebKit struct UrlWebView: UIViewRepresentable { typealias UIViewType = WKWebView var urlToDisplay: URL func makeUIView(context: Context) -> WKWebView { let webView = WKWebView() webView.load(URLRequest(url: urlToDisplay)) return webView } func updateUIView(_ uiView: WKWebView, context: Context) { } } |