Posts

Post not yet marked as solved
1 Replies
273 Views
Running into a weird issue with TabViews not rerendering the view when objectWillChange.send() is called (either manually or with @Published). For context, in my real project I have a tab with form data and the adjacent tab is a summary tab which renders a few elements from the form data. The summary tab is not getting updated when the form data changes. I have created a simple demo project that demonstrates the issue. The project can be found here. The content view is just a tab view with four tabs, all of which point to the same core data object. struct ContentView: View {     @Environment(\.managedObjectContext) private var viewContext     @State private var selectedIndex: Int = 0     var tabTitles: Array<String> = ["Tab 1", "Tab 2", "Tab 3", "Tab 4"]     var body: some View {         // Create a page style tab view from the tab titles.         TabView(selection: $selectedIndex) {             ForEach(tabTitles.indices, id: \.self) { index in                 TextView(viewModel: TextViewModel(                     title: tabTitles[index],                     context: viewContext))             }         }         .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))     } } The text view just contains the title and a text field for updating the core data object in the view model. struct TextView: View {     @ObservedObject private var viewModel: TextViewModel     @State private var text: String     private var relay = PassthroughSubject<String, Never>()     private var debouncedPublisher: AnyPublisher<String, Never>     init(viewModel: TextViewModel) {         self.viewModel = viewModel         self._text = State(initialValue: viewModel.textValue)         self.debouncedPublisher = relay             .debounce(for: 1, scheduler: DispatchQueue.main)             .eraseToAnyPublisher()     }     var body: some View {         LazyVStack {             Text(viewModel.title)                 .font(.title)             TextField("write something", text: $text)                 .onChange(of: text) {                     relay.send($0)                 }         }         .padding()         .onReceive(debouncedPublisher) {             viewModel.textValue = $0         }         /// Without this the view does not update, with it the view updates properly...         .onAppear {             self.text = viewModel.textValue         }     } } And the view model is pretty simple. I've tried making item @Published, I've tried making the text value computed (with @Published item), etc. This example uses a combine publisher on item's value attribute which is a lot like what I'm doing in the main project. class TextViewModel: ObservableObject {     @Published var textValue: String {         didSet {             // Check that the new value is not the same as the current item.value or else an infinte loop is created.             if item.value != textValue {                 item.value = textValue                 try! context.save()             }         }     }     private(set) var item: Item     private(set) var title: String     private var subscriber: AnyCancellable?     private var context: NSManagedObjectContext     init(title: String, context: NSManagedObjectContext) {         self.title = title         self.context = context         let request = NSFetchRequest<Item>(entityName: "Item")         request.predicate = NSPredicate(format: "%K == %@", #keyPath(Item.key), "key")         request.sortDescriptors = [NSSortDescriptor(key: #keyPath(Item.value), ascending: true)]         let fetched = try! context.fetch(request)         let fetchedItem = fetched.first!         self.textValue = fetchedItem.value!         self.item = fetchedItem         // Create a publisher to update the text value whenever the value is updated.         self.subscriber = fetchedItem.publisher(for: \.value)             .sink(receiveValue: {                 if let newValue = $0 {                     self.textValue = newValue                 }             })     } } Item is just a simple core data property with a key: String and value: String. I know I can directly bind the view to to the text value using $viewModel.textValue. It doesn't update the view when the value changes either and I don't want that behavior in my real app for a variety of reasons. Is there something that I am missing here? Do I really need to call onAppear for all of my views within the TabView to check and see if the value is up-to-date and update it if needed? It seems a bit silly to me. I haven't really found much info out there on this. I've also tried forcing a redraw using the (super yucky) use of @State var redraw: Bool and toggling it in onAppear. That does not trigger a redraw either. The other thing I've tried that works is setting an @State isSelected: Bool on the TextView and in the ForEach setting it to index == selectedIndex. This works and may be the least revolting solution I have found. Thoughts?
Posted Last updated
.
Post not yet marked as solved
1 Replies
201 Views
I merged two branches with different feature sets. I branched from one of the two then merged in the second (using command line and file merge). Now in Xcode's source control pane it shows two different branches as current. It's not a big deal but I would like to know how to get rid of the top one (at the very least) and preferably to understand why both would be displayed as current. It makes pushing to remotes a bit of a headache because they have the same app name and so I need to make sure I don't push to the wrong branch. I don't particularly want to have to redo things a different way since the branches had a fair number of merge conflicts. Also, if I use Xcode to commit, a bunch of extra files ending with .orig come up as well as many .pbxproj files that I cannot find when I search my file directory.
Posted Last updated
.