News API : https://newsapi.org/
ContentView.swift
//
// 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)
}
}
}
}
NewsFeedModels.swift
//
// 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
}
}
UrlWebView.swift
//
// 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) {
}
}
