Core Data Count SwiftUI - core-data

When I add a new reminder to my list I can not see the count + 1 simultaneously. But when I re-run the program I see the count is correct.
https://vimeo.com/545025225
struct ListCell: View {
var list : CDListModel
#State var count = 0
#State var isSelected: Bool = false
var body: some View {
HStack{
Color(list.color ?? "")
.frame(width: 30, height: 30, alignment: .center)
.cornerRadius(15)
Text(list.text ?? "")
.foregroundColor(.black)
.font(.system(size: 20, weight: .regular, design: .rounded))
Spacer()
Text(String(count))
.foregroundColor(.gray)
.onAppear{
DispatchQueue.main.async {
self.count = list.reminders!.count
}
}
}
}
}

If CDListModel is a CoreData entity, then you can just add this:
#ObservedObject var list : CDListModel
Also remove the State for the count.
Then display the count like this:
Text(String(list.reminders!.count))
As hint: I wouldn't use force unwrapping aswell. Maybe provide a default value instead of force unwrapping

Related

Unable to use ForEach for distinct records from coredata

I have a table that contains 20 songs from 5 different artists, this number can go up and down , now i want a View where i can display a navigation of these unique singers with their images that are also in table.
So i need only distinct records based on artistname, i query coredata but when i use it in View , i get Generic struct 'ForEach' requires that 'NSFetchRequest<NSDictionary>' conform to 'RandomAccessCollection'
Now to overcome this i use id:.self, this also does not work.
Now i read and learn that ForEach does not like any TYPE that is not sorted , but i have not been able to find a way around this, can any one kindly suggest any solutions, thanks.
This is how i fetch the unique records from core data
let fetchRequest: NSFetchRequest<NSDictionary> = NSFetchRequest(entityName: "Artists")
init(songLVM: Binding<SongListVM>){
_songLVM = songLVM
fetchRequest.propertiesToFetch = ["artistname"]
fetchRequest.returnsDistinctResults = true
fetchRequest.resultType = .dictionaryResultType
}
Below is the entire file
import Foundation
import SwiftUI
import CoreData
struct ArtistList: View {
#Environment(\.managedObjectContext) var managedObjectContext
#Binding var songLVM: SongListVM
#State var namesFilter = [String]()
//-----
let fetchRequest: NSFetchRequest<NSDictionary> = NSFetchRequest(entityName: "Artists")
init(songLVM: Binding<SongListVM>){
_songLVM = songLVM
fetchRequest.propertiesToFetch = ["artistname"]
fetchRequest.returnsDistinctResults = true
fetchRequest.resultType = .dictionaryResultType
}
//-----
var body: some View {
List {
ForEach(fetchRequest) { <---- Error here
idx in
NavigationLink(
destination: ArtistSongs(artistName: idx.artistname ?? "no name", songLVM: $songLVM)) {
ZStack(alignment: .bottomTrailing) {
Image(uiImage: UIImage(data: idx.artistImage ?? Data()) ?? UIImage())
.resizable()
.frame(width: 350, height: 350, alignment: .center)
Text(idx.artistname ?? "no name")
.font(Font.system(size: 50, design: .rounded))
.foregroundColor(.white)
}
}
}
}
}
}
once you figure out what your dictionary is, try this in your ForEach loop:
ForEach(dict.sorted(by: >), id: \.key) { key, value in
...
}
EDIT1:
#FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Artists.artistname, ascending: true)],
animation: .default)
private var artists: FetchedResults<Artists>
...
ForEach(artists, id: \.self) { artist in
...
}

Core Data, Problem with updating (duplicating instead)

I am new to Swift UI. Could you please help me with core data updating?
Here is the point of a problem:
I am building a WatchOS app. There are 3 Views there:
FirstView - a view with a button to Add a new Goal and a List of added Goals.
AddGoalView - appears after pressing Add new Goal.
RingView - a view with a Goal Ring (similar to activity ring mechanics) and all the data presented.
The point of the problem is the next:
After adding a new Goal everything is alright. The Data passes correctly from AddGoalView to the FirstView. I need only 2 items to be passed out of AddGoalView (One String and one Double).
Then, after pressing on the recently created Goal I appear on the Ring View. I successfully pass there 2 items (that String and Double I mentioned).
On the RingView I want to update the 3-rd Item (double) and send it back. So it can be updated on the FirstView.
the Result:
Instead of updating this 3-d Item it just seems to create a completely new Goal on the First View below the previous Goal. Photo
My Code (FirstView):
struct FirstView: View {
#FetchRequest (
entity:NewGoal.entity(),
sortDescriptors:[NSSortDescriptor(keyPath: \NewGoal.dateAdded, ascending: false)],
animation: .easeInOut )
var results:FetchedResults<NewGoal>
#State var showMe = false
var body: some View {
ScrollView{
VStack{
VStack(alignment: .leading){
Text("My Goals:")
NavigationLink(
destination: AddGoalView(),
isActive: $showMe,
label: {
Image(systemName: "plus")
Text("Set Money Goal")
})
Text("Recents:")
ForEach(results){ item in
VStack(alignment: .leading){
NavigationLink(
destination: RingView(GTitle: item.goalTitle ?? "", Sum: item.neededSum),
label: {
HStack{
Image(systemName: "gear")
VStack(alignment: .leading){
Text(item.goalTitle ?? "")
HStack{
Text("$\(item.yourSum, specifier: "%.f")") ///This item doesn't update
Text("/ $\(item.neededSum, specifier: "%.f")")
}
}
}
})
}
}
}
}
}
}
}
My Code (AddGoalView):
struct AddGoalView: View {
#State private var goalTitle = ""
#State private var showMe:Bool = true
#State private var neededSum:Double = 0.0
#State private var isFocusedNum = false
#Environment(\.managedObjectContext) var context
#Environment(\.presentationMode) var presentationMode
var body: some View {
ScrollView{
VStack (alignment: .leading, spacing: 6){
TextField("Goal Name...", text: $goalTitle)
HStack{
Text("$\(neededSum, specifier: "%.f")")
.overlay(
RoundedRectangle(cornerRadius: 9)
.stroke(isFocusedNum ? Color.red : Color.white, lineWidth: 1)
.opacity(1.0))
.focusable(true) { newState in isFocusedNum = newState}
.animation(.easeInOut(duration: 0.1), value: isFocusedNum)
.digitalCrownRotation(
$neededSum,
from: 0,
through: 100000,
by: 25,
sensitivity: .high)
}
Button(action: addGoal) {
Text("Add Goal")
}
.disabled(neededSum == 0.0)
.disabled(goalTitle == "")
.navigationTitle("Edit")
}
}
}
private func addGoal(){
let goal = NewGoal(context: context)
goal.goalTitle = goalTitle
goal.dateAdded = Date()
goal.neededSum = neededSum
do{
try context.save()
presentationMode.wrappedValue.dismiss()
}catch let err{
print(err.localizedDescription)
}
}
My Code (RingView Code):
struct RingView: View {
#State private var isFocusedSum = false
#State private var yournewSum:Double = 0.0
var goalItem: NewGoal?
var Sum:Double
var GTitle:String
#Environment(\.managedObjectContext) var context
#Environment(\.presentationMode) var presentationMode
#FetchRequest var results: FetchedResults<NewGoal>
init(GTitle: String, Sum: Double){
self.GTitle = GTitle
self.Sum = Sum
let predicate = NSPredicate(format:"goalTitle == %#", GTitle)
self._results=FetchRequest(
entity: NewGoal.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \NewGoal.dateAdded, ascending: false)],
predicate: predicate,
animation: .easeInOut
)
}
var body: some View {
ZStack{
ForEach(results) { item in
RingShape(percent:(yournewSum/item.neededSum*100), startAngle: -90, drawnClockwise: false) /// Ring
.stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round))
.fill(AngularGradient(gradient: Gradient(colors: [.red, .pink, .red]), center: .center))
.frame(width: 155, height: 155)
HStack(alignment: .top){
Spacer()
Button(action: addSum) { ///BUTTON TO Update
Image(systemName: "gear")
}
.clipShape(Circle())
}
VStack(alignment: .trailing, spacing: 0.0){
Spacer()
Text("$\(yournewSum, specifier: "%.f")") /// Here is the data I want to change via Digital Crown and update
.font(.title3)
.overlay(
RoundedRectangle(cornerRadius: 7)
.stroke(Color.white, lineWidth: 2)
.opacity(isFocusedSum ? 1.0:0.0)
)
.focusable(true) { newState in isFocusedSum = newState}
.animation(.easeInOut(duration: 0.3), value: isFocusedSum)
.digitalCrownRotation(
$yournewSum,
from: 0,
through: Double((item.neededSum)),
by: 10,
sensitivity: .high)
Text("/ $\(item.neededSum, specifier: "%.f")") ///Here is the Double data I entered in AddGoalView
.font(.caption)
}
.frame(width: 200, height: 230)
.padding(.top, 7)
VStack(alignment: .center, spacing: 1.0){
Text(item.goalTitle ?? "Your Goal Name") ///Here is the String data I entered in AddGoalView
.foregroundColor(.gray)
}
.padding(.top, 200.0)
}
}
.padding([.top, .leading, .trailing], 5.0)
}
private func addSum(){
let goal = goalItem == nil ? NewGoal(context: context): goalItem
goal?.yourSum = yournewSum //// I am trying to update the Data here, but after running the func it creates a duplicate.
do{
try context.save()
presentationMode.wrappedValue.dismiss()
} catch let err{
print(err.localizedDescription)
}
}
You never give var goalItem: NewGoal? the initial value of the item you want to update.
try replacing this
RingView(GTitle: item.goalTitle ?? "", Sum: item.neededSum)
with
RingView(goalItem: item, GTitle: item.goalTitle ?? "", Sum: item.neededSum)
and of course your have to change your initializer for RingView to
init(goalItem: NewGoal? = nil, GTitle: String, Sum: Double){
and add to the initializer this line
self.goalItem = goalItem

SwiftUI Picker VALUE

I've been trying to program something with swiftui recently,
it's difficult, how can I transfer the value of the picker into the
text field, I'm desperate!
and why can I not work with the value $ khValue directly as in the text field?
I've already spent hours searching the internet… I haven't found anything yet, swiftUI is completely different from swift
import SwiftUI
struct KH_aus_Co2: View {
#State private var kh_Picker : String = ""
#State private var ph_Picker: String = ""
var kh_vol = [Int](0..<21)
var ph_vol = [Int](0..<10)
init(){
UITableView.appearance().backgroundColor = .clear
}
#State private var khWert: String = ""
#State private var phWert: String = ""
#State private var phco2Wert: String = ""
var calculation: String {
guard khWert.isEmpty == false, phWert.isEmpty == false else { return "" }
guard let kh = Double(khWert), let ph = Double(phWert) else { return "Error" }
let product = kh/2.8 * pow(10,7.90-ph)
return String(format: "%.2f", product)
}
var body: some View {
VStack() {
Text("Co2 = \(calculation) mg/ltr")
.font(.largeTitle)
.multilineTextAlignment(.center)
.foregroundColor(Color.green)
.frame(width: 300, height: 60, alignment: .center)
.border(Color.green)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
HStack {
TextField("KH Wert", text: $khWert)
.border(Color.green)
.frame(width: 120, height: 70, alignment: .center)
.textFieldStyle(RoundedBorderTextFieldStyle())
// .textContentType(.oneTimeCode)
.keyboardType(.numberPad)
TextField("PH Wert", text: $phWert)
.border(Color.green)
.frame(width: 120, height: 70, alignment: .center)
.textFieldStyle(RoundedBorderTextFieldStyle())
// .textContentType(.oneTimeCode)
.keyboardType(.numberPad)
}
GeometryReader { geometry in
HStack {
Picker(selection: self.$kh_Picker, label: Text("")) {
ForEach(0 ..< self.kh_vol.count) { index in
Text("\(self.kh_vol[index])").tag(index)
//
}
}
.frame(width: geometry.size.width/3, height: 100, alignment: .center) .clipped()
Picker(selection: self.$ph_Picker, label: Text("")) {
ForEach(0 ..< self.ph_vol.count) { index in
Text("\(self.ph_vol[index])").tag(index)
}
}
.frame(width: geometry.size.width/3, height: 100, alignment: .center) .clipped()
}
}
}
.navigationBarTitle(Text("Co2 aus KH & PH"))
.font(/*#START_MENU_TOKEN#*/.title/*#END_MENU_TOKEN#*/)
}
}
struct KH_aus_Co2_Previews: PreviewProvider {
static var previews: some View {
Co2_aus_KH()
}
}
big thanks for your help
Jürgen....................................
If I understand you correctly, you want the pickers to update the values in the textfields. If so, then you want to bind the same value to the picker that the textfield is using. Since that value is a String, you will want to use String values in your picker.
Use .map(String.init) to turn the kh_vol and ph_vol ranges into arrays of String, and use \.self as the id::
Picker(selection: self.$khWert, label: Text("")) {
ForEach(self.kh_vol.map(String.init), id: \.self) { index in
Text(index)
}
}
.frame(width: geometry.size.width/3, height: 100, alignment: .center)
.clipped()
Picker(selection: self.$phWert, label: Text("")) {
ForEach(self.ph_vol.map(String.init), id: \.self) { index in
Text(index)
}
}
.frame(width: geometry.size.width/3, height: 100, alignment: .center)
.clipped()

edit a Core Data Object in SwiftUI

I push an object that comes from the Core Data Database to a detail page with text fields.
When the user changes the text in the textfields and presses save the changes should be saved to the core data DB.
Problem: I have no clue how to modify/update/change an existing core data entity.
I probably need to get the original via a #FetchRequest but every time I try I get some problems.
Question 1: Let's say the entity has object.id as UUID, how can I
fetch that object in SwiftUI?
Question 2: How can I overwrite the fetched object with the changed
content of the textfields?
struct ProductDetail: View {
#State var barcode: Barcode
#Environment(\.presentationMode) var presentationMode
#Environment(\.managedObjectContext) var context
//let datahandler = Datahandler()
var body: some View {
VStack {
HStack {
Text("Barcode: ")
Spacer()
TextField("Barcode", text: Binding($barcode.code, ""))
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 250, alignment: .trailing)
}
HStack {
Text("Amount: ")
Spacer()
TextField("Amount", text: Binding($barcode.amount, ""))
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 250, alignment: .trailing)
.keyboardType(.numberPad)
}
HStack{
Button("Back") {
self.presentationMode.wrappedValue.dismiss()
}
Spacer()
Button("Save"){
//self.datahandler.updateBarcode(barcode: self.object)
self.editBarcode(barcode: barcode)
self.presentationMode.wrappedValue.dismiss()
}
}
.padding()
}
.padding()
}
func editBarcode(barcode: Barcode) {
// Question 1: Fetch Original object using barcode.id
// Question 2: How to but barcode into context so it can overwrite the core data original?
try? context.save()
}
Attempt:
func editBarcode(barcode: Barcode) {
#FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "self.id IN %#", barcode.id)) var results: FetchedResults<Barcode>
results.first?.amount = barcode.amount
results.first?.code = barcode.code
try? context.save()
}
Errors:
Argument type 'UUID?' does not conform to expected type 'CVarArg'
Cannot use instance member 'barcode' within property initializer;
property initializers run before 'self' is available
Property wrappers are not yet supported on local properties

Same image being shown when showing a modal via .sheet after iterating array

In a swiftui app, I am iterating an array of entities and showing thumbmnail images and have it set up that when one is tapped, a detail view is shown with that particular full size image. The problem is that it's always the most recent image being shown when going to the detail screen.
Main view:
if documents.count > 0 {
ScrollView(.horizontal, showsIndicators: true) {
HStack {
ForEach(documents, id: \.self.id) {(doc: Document) in
Image(uiImage: UIImage(data: doc.image)!)
.resizable()
.frame(width: 40, height: 50, alignment: .center)
.clipShape(Rectangle())
.cornerRadius(8)
.scaledToFit()
.onTapGesture {
self.showImageDetail = true
}
.padding(.all, 5)
.sheet(isPresented: self.$showImageDetail, content: {
ImageViewDetail(image: UIImage(data: doc.image)!)
})
}
}
}
}
When ImageDetailView is shown, it's alway the most recent image saved. Below is the detail view code:
import SwiftUI
struct ImageViewDetail: View {
#Environment(\.presentationMode) var presentationMode
#State var image: UIImage
#State var scale: CGFloat = 1.0
var body: some View {
VStack {
Image(uiImage: image)
.resizable()
.padding()
.scaledToFit()
.scaleEffect(scale)
.cornerRadius(8)
.gesture(MagnificationGesture()
.onChanged {value in
self.scale = value.magnitude
}
)
HStack {
Button("Back") {
self.presentationMode.wrappedValue.dismiss()
}
.buttonStyle(FillStyle(width: 86, height: 32))
.padding(.trailing, 10)
Button("Delete") {
}
.buttonStyle(FillStyle(width: 86, height: 32))
.padding(.leading, 10)
}
}
.navigationBarTitle("Image", displayMode: .inline)
}
}
Any help would be greatly appreciated - I can't seem to see why the right image is not displayed by the ImageDetailView? Many thanks in advance.
Replace the Bool state with a Document? state:
#State var selectedDocument: Document?
var body: some View {
HStack {
ForEach(documents, id: \.self.id) {(doc: Document) in
Image(uiImage: UIImage(data: doc.image)!)
.onTapGesture {
self.selectedDocument = doc
}
}
}
}
.sheet(item: $selectedDocument) {
ImageViewDetail(image: UIImage(data: $0.image)!)
}
}
Sheet can be only one in view hierarchy, so below it is relocated for HStack and it is needed to add member currentImage
Here is scratchy approach... (not tested - might be needed take care of optionals)
#State private var currentImage: UIImage = UIImage() // < needed optionals?
...
ScrollView(.horizontal, showsIndicators: true) {
HStack {
ForEach(documents, id: \.self.id) {(doc: Document) in
Image(uiImage: UIImage(data: doc.image)!)
.resizable()
.frame(width: 40, height: 50, alignment: .center)
.clipShape(Rectangle())
.cornerRadius(8)
.scaledToFit()
.onTapGesture {
self.currentImage = UIImage(data: doc.image) ?? UIImage() // < current
self.showImageDetail = true
}
.padding(.all, 5)
}
}
.sheet(isPresented: self.$showImageDetail, content: { // < one sheet
ImageViewDetail(image: self.currentImage)
})
}

Resources