Skip to content

Instantly share code, notes, and snippets.

@jordansinger
Last active August 15, 2020 22:12
Show Gist options
  • Select an option

  • Save jordansinger/838a8f7be874ced4e351e9f07eb26d8e to your computer and use it in GitHub Desktop.

Select an option

Save jordansinger/838a8f7be874ced4e351e9f07eb26d8e to your computer and use it in GitHub Desktop.

Revisions

  1. jordansinger revised this gist Jul 4, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion News.swift
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    import SwiftUI
    import Combine
    import SafariServices
    import PlaygroundSupport

  2. jordansinger created this gist Jul 4, 2020.
    151 changes: 151 additions & 0 deletions News.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,151 @@
    import SwiftUI
    import Combine
    import SafariServices
    import PlaygroundSupport

    // lil news api
    let apiURL = "https://api.lil.software/news"

    struct News: Codable {
    var articles: [Article]
    }

    struct Article: Codable, Hashable {
    var title: String
    var url: String
    var image: String?
    var source: String
    }

    struct NewsView: View {
    @State var articles: [Article] = []
    @State var loading = true

    var body: some View {
    NavigationView {
    VStack {
    if self.loading {
    Text("Loading...")
    } else {
    List {
    ForEach(articles, id: \.self) { article in
    ArticleView(article: article)
    }
    }
    .navigationBarTitle("News", displayMode: .inline)
    }
    }
    }
    .navigationViewStyle(StackNavigationViewStyle())
    .onAppear(perform: {
    self.loadNews()
    })
    }

    func loadNews() {
    // create the API url (ex: https://api.lil.software/news)
    let request = URLRequest(url: URL(string: apiURL)!)

    // initiate the API request
    URLSession.shared.dataTask(with: request) { data, response, error in
    if let data = data {
    // decode the response into our News struct
    if let decodedResponse = try? JSONDecoder().decode(News.self, from: data) {
    DispatchQueue.main.async {
    // set the articles based on the API response
    self.articles = decodedResponse.articles
    // we're no longer "loading"
    self.loading = false
    }

    return
    }
    }
    }.resume()
    }
    }

    struct ArticleView: View {
    @State var article: Article
    @State var showWebView = false

    var body: some View {
    Button(action: { self.showWebView = true }) {
    VStack (alignment: .leading, spacing: 24) {
    HStack(alignment: .top, spacing: 16) {
    ImageView(imageUrl: article.image ?? "")
    .frame(width: 128, height: 96)
    .background(Color(UIColor.systemFill))
    .cornerRadius(4)
    .clipped()

    VStack (alignment: .leading, spacing: 6) {
    Text(article.title)
    .font(.system(.headline, design: .serif))
    Text(article.source)
    .font(.callout)
    .lineLimit(1)
    .foregroundColor(.secondary)
    }
    }
    }
    .padding(.vertical, 12)
    }
    .buttonStyle(PlainButtonStyle())
    .sheet(isPresented: $showWebView) {
    WebView(url: URL(string: self.article.url)!)
    }
    }
    }

    struct WebView: UIViewControllerRepresentable {
    // to display an article's website
    let url: URL

    func makeUIViewController(context: UIViewControllerRepresentableContext<WebView>) -> SFSafariViewController {
    let webview = SFSafariViewController(url: url)
    return webview
    }

    func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<WebView>) {
    // nothing to do
    }
    }

    struct ImageView: View {
    @ObservedObject var remoteImageURL: RemoteImageURL

    init(imageUrl: String) {
    remoteImageURL = RemoteImageURL(imageURL: imageUrl)
    }

    var body: some View {
    Image(uiImage: UIImage(data: self.remoteImageURL.data) ?? UIImage())
    .resizable()
    .scaledToFit()
    .aspectRatio(contentMode: .fill)
    }
    }

    class RemoteImageURL: ObservableObject {
    @Published var data = Data()

    // load our image URL
    init(imageURL: String) {
    guard let url = URL(string: imageURL.replacingOccurrences(of: "http://", with: "https://")) else {
    print("Invalid URL")
    return
    }

    let request = URLRequest(url: url)
    URLSession.shared.dataTask(with: request) { data, response, error in
    if let data = data, let response = response {
    DispatchQueue.main.async {
    self.data = data
    }
    }
    }.resume()
    }
    }

    PlaygroundPage.current.setLiveView(NewsView())