TableViewController:
import UIKit
class NewsTableViewController: UITableViewController {
var newsItems: [NewsItem] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "NewsCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newsItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell", for: indexPath)
let newsItem = newsItems[indexPath.row]
cell.textLabel?.text = newsItem.title
cell.detailTextLabel?.text = formatDate(newsItem.date)
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
return cell
}
private func formatDate(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
return formatter.string(from: date)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newsItem = newsItems[indexPath.row]
let alert = UIAlertController(title: newsItem.title, message: newsItem.content, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
}
Update to add NewsListViewModel
import Foundation
import Parse
class NewsListViewModel: ObservableObject {
@Published var newsItems: [NewsItem] = []
func fetchNews() {
let query = PFQuery(className: "News")
query.order(byDescending: "updatedAt")
query.findObjectsInBackground { (objects, error) in
if let error = error {
print("Error fetching news: (error.localizedDescription)")
} else if let objects = objects {
let fetchedNews = objects.compactMap { object -> NewsItem? in
guard let title = object["title"] as? String,
let date = object.updatedAt,
let description = object["description"] as? String else {
return nil
}
return NewsItem(id: UUID(), title: title, date: date, content: description)
}
DispatchQueue.main.async {
self.newsItems = fetchedNews
print("Fetched (fetchedNews.count) news items")
}
}
}
}
}
I really have no clue what’s going on here. I get objects from Parse, it shows them in a table view controller, and when I tap a row it opens a sheet. The sheet is supposed to display the content of the news item, which is from the key description. For some reason, it jumps straight to the else statement of the if let.
struct NewsAndAlertsView: View {
@ObservedObject private var viewModel = NewsListViewModel()
@State private var selectedNewsItem: NewsItem? = nil
@State private var isPresentingModal = false
var body: some View {
VStack {
if viewModel.newsItems.isEmpty {
Text("Loading news...")
.font(.headline)
.padding()
} else {
NewsListView(newsItems: viewModel.newsItems) { newsItem in
print("Selected News Item: (newsItem.title)")
self.selectedNewsItem = newsItem
self.isPresentingModal = true
}
}
}
.navigationBarTitle("News & Alerts", displayMode: .inline)
.onAppear {
viewModel.fetchNews()
}
.sheet(isPresented: $isPresentingModal) {
if let newsItem = self.selectedNewsItem {
VStack {
Text(newsItem.title)
.font(.largeTitle)
.padding()
Text(formatDate(newsItem.date))
.font(.subheadline)
.padding()
Text(newsItem.content)
.padding()
Spacer()
}
.padding()
.onAppear {
print("Sheet Title: (newsItem.title)")
print("Sheet Date: (formatDate(newsItem.date))")
print("Sheet Content: (newsItem.content)")
}
} else {
Text("No News Item Selected")
}
}
}
When I’ve done debug for self.selectedNewsItem I get this:
Optional(ChurchApp.NewsItem(id: 2ECF3231-2DB9-4F28-8B4C-6DC1305D50B2, title: "Test", date: 2024-08-06 02:07:05 +0000, content: "Just a test"))
Again the issue seems to be related to the if let newsItem = self.selectedNewsItem {
line of code. But as I’m new to Swift, I’m lost.
2
Answers
This behavior in SwiftUI might not be ideal (in my opinion), but you can work around it using a different API designed for this purpose.
You can combine your presentation logic with the
nil
check like this:Note that
NewsItem
must conform toIdentifiable
for this to work. If it doesn’t already conform, you can make it conform like this:You can extract the views in the sheet and use a binding variable in that extracted view. Using a binding will update the view when the variable is updated:
The extracted view code: