-
-
Save Lyncore/63c4bf8c07d97a473695f2a47267719e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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