Skip to content

Instantly share code, notes, and snippets.

@Lyncore
Last active February 4, 2023 14:11
Show Gist options
  • Save Lyncore/63c4bf8c07d97a473695f2a47267719e to your computer and use it in GitHub Desktop.
Save Lyncore/63c4bf8c07d97a473695f2a47267719e to your computer and use it in GitHub Desktop.
import SwiftUI
import MarkdownUI
import Combine
enum Resource<T> {
case success(T)
case loading
case error(String)
}
extension Resource: Equatable {
static func ==(lhs: Resource, rhs: Resource) -> Bool {
switch (lhs, rhs) {
case (.success, .success): return true
case (.loading, .loading): return true
case (.error, .error): return true
default: return false
}
}
}
struct NewsItem{
let id: Int
let title, createdAt: String
let content: MarkdownContent
}
struct ContentView: View {
@StateObject private var viewModel: ContentView.ViewModel = .init()
var body: some View {
NavigationView{
if case let .success(result) = viewModel.items {
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 8) {
ForEach(result, id: \.id) { item in
VStack(alignment: .leading) {
HStack(alignment: .top) {
Text(item.title).font(.title3).foregroundColor(.black)
Spacer()
Text(item.createdAt).foregroundColor(Color.accentColor)
}
Markdown(item.content).markdownTheme(Theme()
.link {
ForegroundColor(Color.accentColor)
}
.text {
ForegroundColor(Color.gray)
}
)
}
.padding(8)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.padding(.horizontal, 8)
}
}
.padding(.vertical, 8)
}
.background(Color.gray)
.navigationTitle("News")
} /*
else show loading indicator or error message, removing this selector does not affect the result
*/
}
}
}
extension ContentView {
class ViewModel: ObservableObject {
private var bag: Set<AnyCancellable> = .init()
@Published var items: Resource<[NewsItem]> = .loading
init(){
fetch()
.assign(to: \.self.items, on: self)
.store(in: &self.bag)
}
/*
emulate network logic:
each item can be a different size, can contains links and images
*/
private let loren = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin fermentum, urna sed auctor auctor, justo tellus feugiat elit, a ultricies tortor justo ac orci. Nulla risus turpis, ullamcorper in eros dictum, blandit ultrices tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sagittis varius urna vitae aliquet. Nullam laoreet mollis vulputate. Quisque eget enim pharetra, molestie augue at, semper purus. Pellentesque bibendum pharetra nisi, nec condimentum ante sodales sed. Integer congue tortor sed urna pulvinar porttitor id a arcu. Aliquam ut quam pellentesque, ultricies arcu a, vestibulum velit. "
private func fetch() -> AnyPublisher<Resource<[NewsItem]>, Never> {
var data: [NewsItem] = []
for i in 1...30 {
data.append(.init(
id: i,
title: "Loren Ipsum Title \(i)", //title height does not affect the result
createdAt: "10.01.2023",
content: MarkdownContent(String(loren.prefix(Int.random(in: 300...600))))
))
}
return Just(Resource.success(data)).eraseToAnyPublisher()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment