Posts

Post not yet marked as solved
1 Replies
317 Views
class PostManager: ObservableObject { static let shared = PostManager() private init() {} @Published var containers: [PostContainer] = [] // Other code } class PostContainer: ObservableObject { var id: UUID = UUID() var timestamp: Date var subreddit: String var posts: [Post] var type: ContainerType var active: Bool // Init } @Model final class Post: Decodable, ObservableObject { // Other code } In my main view, a network request is made and a PostContainer is created if it doesn't exist. This code works fine, and the view is updated correctly. let container = PostContainer(timestamp: Date(), subreddit: subreddit, posts: posts, type: .search, active: true) self.containers.append(container) If the user wants to see more, they press a button and another request is made. This time, the new data will be added to the PostContainer instead of creating a new one. if let container = self.containers.first(where: {$0.subreddit == subreddit}) { // Update previous container container.timestamp = Date() print(container.posts.count) // Map IDs from container and then remove duplicates let existingIDs = Set(container.posts.map { $0.id }) let filtered = posts.filter { !existingIDs.contains($0.id) } // Append new post container.posts.append(contentsOf: filtered) container.active = true } This code is working fine as well, except for the view is not updating. In the view, PostManager is an @EnvironmentObject. I also have a computed variable to get the post and sort them. I added a print statement to that variable and saw that it wasn't being printed even though more data was being added to the PostContainer. At one point, I created an ID for the List that displays the data and had the code inside PostManager update that ID when it was finished. This of course worked, but it's not ideal. How can I get the view to update when post are appended inside of PostContainer?
Posted Last updated
.
Post not yet marked as solved
1 Replies
620 Views
I have my ContentView which has a Sheet that will appear when a button is pressed. struct ContentView: View { @EnvironmentObject private var settings: SettingsHandler @State private var settingsView: Bool = false var body: some View { NavigationStack { Button(action: { settingsView.toggle() }, label: { Image(systemName: "gearshape.fill") }) } .preferredColorScheme(settings.darkTheme ? .dark : nil) .sheet(isPresented: $settingsView, content: { SettingsView() }) } } Let's say the app is in light mode based on the phones theme settings. You open the SettingsView and select the toggle that will switch to dark mode. Everything changes to dark mode, including the SettingsView sheet. Then you select the same toggle to switch back and ContentView in the background changes to light theme but the sheet doesn't until you close and reopen it. I would think it would change back considering it changed to dark mode without needing to be closed. I tried attaching an ID to the SettingsView and having it refresh when settings.darkTheme is changed, however, it doesn't seem to be doing anything. I also added the .preferredColorScheme() modifier into the SettingsView, but it did nothing. I also replaced the nil to .light, and the same issue occurred. Settings is an EnvironmentObject that I created to manage all the Settings I have. At the moment, I'm thinking I can have the sheet just close and reopen, however, I would like for it to update properly. Any ideas?
Posted Last updated
.
Post not yet marked as solved
1 Replies
1.1k Views
That may not be the best way to explain it. Essentially, I'm requesting data from Reddit and storing it in an object called Request. This object has a timestamp and an array of objects called Post. Everything was working fine until I started to add some code to filter the post that were being fetched from reddit. extension [Request] { func insert(request: Request, in context: ModelContext) { if self.count == 0 { context.logMessage("There are no existing request") context.insert(request) context.logMessage("Request saved") }else { print(request) // No crash print(request.timestamp) // No crash print(request.posts) // Causes a crash } } } When it does crash, it points to this code inside the SwiftData model. This code seems to be something within SwiftData. I didn't write any of this. { @storageRestrictions(accesses: _$backingData, initializes: _posts) init(initialValue) { _$backingData.setValue(forKey: \.posts, to: initialValue) _posts = _SwiftDataNoType() } get { _$observationRegistrar.access(self, keyPath: \.posts) return self.getValue(forKey: \.posts) } set { _$observationRegistrar.withMutation(of: self, keyPath: \.posts) { self.setValue(forKey: \.posts, to: newValue) } } } It has the error at the return self.getValue() line: Thread 5: EXC_BREAKPOINT (code=1, subcode=0x2406965c4) This is the sequence that occurs: View is loaded Checks if it should load a new request If it should, it calls this function private func requestNewData() { redditService.fetchRedditAllData(completion: { result in DispatchQueue.main.async { switch result { case .success(let newRequest): modelContext.logMessage("Successfully retreived and decoded data from Reddit") // Log the success //modelContext.insert(newRequest) requests.insert(request: newRequest, in: modelContext) case .failure: modelContext.logMessage("Failed to retrieve and decode data from Reddit") } } }) } The code for the fetch function is here: func fetchRedditAllData(completion: @escaping (Result<Request, Error>) -> Void) { // Try to construct the URL and return if it fails guard let url = URL(string: RedditRequests.all) else { context.logMessage("Failed to contruct the url for r/all") return } // Try to retrieve data from the URL session.dataTask(with: url, completionHandler: { data, _, error in // If it fails, log the failure if let error = error { self.context.logMessage("Failed to retrieve data from the r/all URL.\n\(error.localizedDescription)") } else { // If it doesn't fail, try to decode the data do { let data = data ?? Data() // Get data let response = try self.decoder.decode(Request.self, from: data) // Decode JSON into Request model completion(.success(response)) // Return response (request) if successful self.context.logMessage("Decoded data") } catch { completion(.failure(error)) self.context.logMessage("Failed to decode data into a Request.\n\(error.localizedDescription)") } } }).resume() } If I don't try to access request.posts before saving it to the context, it works fine. It will fetch the data and store it to the phone and then display it on the phone. When I try to access request.posts to do some filtering, it crashes. Does anyone have any ideas?
Posted Last updated
.
Post marked as solved
2 Replies
359 Views
Hello, I have an app that uses JSON data to save and load information. This is how the information is loaded when the app is first opened. DispatchQueue.global(qos: .background).async { [weak self] in //Get data from "user.data" guard let data = try? Data(contentsOf: Self.fileURL) else { //code to use test data /*#if DEBUG DispatchQueue.main.async { self?.scrums = DailyScrum.data } #endif*/ return } guard let user = try? JSONDecoder().decode(User.self, from: data) else { fatalError("Can't decode user data") } //Set published value to user that was loaded from the file DispatchQueue.main.async { self?.user = user } } } There is a struct called Month that is used inside of another struct called User. When the app is open, in the onAppear in ContentView, it runs a function to check the current date. It compares it against what's already saved, and if it's a new month, it will create a new Month struct to replace the current month. private mutating func newMonth() { //Create new month month = Month(name: Date().month) } This seems to be working, but not exactly as intended. The new month will be created, but when I open the app it will still show the previous month. I have a TextView that displays the month. I printed the month variable to the console to see what it was reading. Printed in an HStack this way let _ = print(user.month) Output Month(name: "July", transactions: [], income: 0.0, expenses: 0.0, net: 0.0, saved: 0.0, invested: 0.0, netMargin: 0.0) Month(name: "June", transactions: [], income: 0.0, expenses: 0.0, net: 0.0, saved: 0.0, invested: 0.0, netMargin: 0.0) Somehow that month variable is showing two different months in it, June and July. It doesn't make sense to me, because it's not an array and that month variable should be getting replaced due to the newMonth() function. What's going on here? How is it creating a new month, but not overwriting the previous month? Let me know if you need anymore information.
Posted Last updated
.
Post not yet marked as solved
1 Replies
449 Views
Hello. I have a picker that uses an enum to populate itself. There is a class called "user" that has a variable called "defaultTab". This is what's being used as the selection for the picker. enum Tab: String, CaseIterable { case data = "Data" case transactions = "Transactions" case user = "User" var id: String {self.rawValue} } That is the enum. import SwiftUI class User: ObservableObject { var name: String var sources: [Source] var accounts: [Account] var categories: [Category] var transactions: [Transaction] var defaultTab: Tab init(name: String, sources: [Source], accounts: [Account], categories: [Category], transactions: [Transaction], defaultTab: Tab) { self.name = name self.sources = sources self.accounts = accounts self.categories = categories self.transactions = transactions self.defaultTab = defaultTab } } That is the class, and below is the picker. Picker("Default tab", selection: $user.defaultTab) { ForEach(Tab.allCases, id: \.self) { kind in Text(kind.rawValue) .tag(kind as Tab) } } This picker is inside of a Section that is inside of a List. When I make a selection, it does not change the picker. How can I resolve this issue?
Posted Last updated
.