My struct values cannot be used in function -> Cannot convert value of type 'Int' to expected argument type 'Range<Int>' - struct

Im getting an error when using my Question struct in my function checkAnswer
I want to use the value in questions[index].answer but it error says
Cannot convert value of type 'Int' to expected argument type 'Range' and Value of type 'ArraySlice' has no member 'answer'
What did I do wrong on that part?
import SwiftUI
struct Question {
var question: String
var answer: Int
}
struct ContentView: View {
#State private var isGameRunning = false
let multiplyTableRange = Range(2...12)
#State private var selectedTable = 2
#State private var selectedNumOfQuestions = 5 //min of 5
#State private var variantsForCountOfQuestions = [5, 10, 20]
#State private var questions = [Question]()
#State private var currentQuestionIndex: Int = 1
#State private var answerInput: Int = 0
#State private var score = 0
#State private var isGameDone = false
#State private var gameCompleteMessage = ""
var body: some View {
if isGameRunning{
Group{
VStack{
Text("Your Score \(score)")
Text("\(questions[currentQuestionIndex].question)")
TextField("Enter your answer", value: $answerInput, formatter: NumberFormatter())
//error here
Button("Enter") {
checkAnswer(userAnswer: answerInput ?? 0 , answer: questions[currentQuestionIndex].answer)
}
.alert(isPresented: $isGameDone) {
Alert(title: Text(gameCompleteMessage), message: Text("Restart game"), primaryButton: .destructive(Text("Okay")) {
isGameRunning = false
answerInput = 0
score = 0
gameCompleteMessage = ""
currentQuestionIndex = 1
}, secondaryButton: .cancel())
}
}
}
}else{
Group{
VStack{
Text("Pick multiplication table to practice")
Picker("Pick multiplication table to practice", selection: $selectedTable){
ForEach(multiplyTableRange, id: \.self){
Text("\($0)")
}
} //eof picker
.labelsHidden()
.pickerStyle(SegmentedPickerStyle())
Text("How many questions?")
Picker("", selection: $selectedNumOfQuestions){
ForEach(variantsForCountOfQuestions, id: \.self){
Text("\($0)")
}
}//eofPicker
.labelsHidden()
.pickerStyle(SegmentedPickerStyle())
Button("Start"){
generateQuestions()
isGameRunning.toggle()
}
}
}
}
} //eof body
func generateQuestions(){
questions.removeAll()
for question in 1...selectedNumOfQuestions {
let x = Question(question: ("\(selectedTable) X \(question)"), answer: selectedTable * question)
questions.append(x)
}
}
func checkAnswer(userAnswer: Int, answer: Int) {
if userAnswer == answer {
score += 1
}
if currentQuestionIndex < selectedNumOfQuestions - 1 {
currentQuestionIndex += 1
} else {
gameCompleteMessage = "Your score is \(score)"
isGameDone = true
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related

multiply two value from textfield by using core data in swiftui

i try to make an invoice app by using core data, i have 4 attribute: Article String, Price String , Quantity String and Taxe String and i try to make a fonction to multiply price * quantity and the result of them i want to make another function for calculate the taxe for each article
Like this ->
Article:
Price $:
Quantity x:
Taxe %:
Total taxe %:
Total price taxe incl $:
and then at the end i want sum total price and total taxe of my list created by user in textfield in 2 diffrent textView
like this ->
Total taxe %:
Total prie incl taxe $:
struct Facturation: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(entity: FacturationCoreData.entity(), sortDescriptors: []) private var factures: FetchedResults<FacturationCoreData>
#State private var article = ""
#State private var prix = ""
#State private var quantite = ""
#State private var tvac = ""
#State private var tva = [0, 6, 12, 21]
#State private var tipIndex = 2
#AppStorage("textChange") private var textChange = ""
func prixQuantite() -> Double {
let fact = FacturationCoreData(context: viewContext)
let prix = fact.prix ?? ""
let quan = fact.quantite ?? ""
let sum = ((Double(prix) ?? 00) * (Double(quan) ?? 00))
return sum
}
func calculeTvaC() ->Double {
let fact = FacturationCoreData(context: viewContext)
let prixEtQuantite = prixQuantite()
let tva = fact.tva ?? ""
let prixTTC = ((prixEtQuantite / 100 * (Double(tva ?? "") ?? 00)) +
prixEtQuantite)
return prixTTC
}
var body: some View {
NavigationView{
VStack {
Form {
Section("Article"){
TextField("Article", text: $article)
.disableAutocorrection(true)
}
HStack{
Section("Q"){
TextField("Quantite", text: $quantite)
.keyboardType(.decimalPad)
}
Section("P") {
TextField("Prix", text: $prix)
.keyboardType(.decimalPad)
}
Section("T") {
TextField("TVA", text: $tvac)
.keyboardType(.decimalPad)
}
}
Button {
let facture = FacturationCoreData(context: viewContext)
facture.article = article
facture.quantite = quantite
facture.prix = prix
facture.tva = tvac
do{
try viewContext.save()
}catch{
print(error)
}
} label: {
Image(systemName: "square.and.arrow.down.fill")
.font(.title)
}
List{
ForEach(factures) { facture in
VStack(alignment: .leading){
Text("Artile: " + (facture.article ?? ""))
Text("Quantité: " + (facture.quantite ?? "") + "x")
Text("Prix: " + (facture.prix ?? "") + "€")
Text("Tva: " + (facture.tva ?? "") + "%")
Text("Total price:\(prixQuantite())$ ")
Text("Total taxe:\(calculeTvaC())$ ")
}.onTapGesture {
textChange = facture.article ?? ""
}
}.onDelete { indexSet in
indexSet.forEach { index in
let deletfacture = factures[index]
viewContext.delete(deletfacture)
do{
try viewContext.save()
}catch{
print(error)
}
}
}
}
}
Text(textChange)
}
.navigationTitle("Facturation")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink {
Client()
} label: {
Image(systemName: "person.badge.plus")
}
}
ToolbarItem(placement: .navigationBarLeading) {
NavigationLink {
ClientList()
} label: {
Image(systemName: "list.bullet")
}
}
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
}
}
}
You can use an extension
extension FacturationCoreData{
var prixQuantite : Double {
let prix = self.prix ?? ""
let quan = self.quantite ?? ""
let sum = ((Double(prix) ?? 00) * (Double(quan) ?? 00))
return sum
}
var calculeTvaC : Double {
let prixEtQuantite = prixQuantite
let tva = self.tva ?? ""
let prixTTC = ((prixEtQuantite / 100 * (Double(tva ?? "") ?? 00)) +
prixEtQuantite)
return prixTTC
}
}
Then you can use
Text("Total price:\(facture.prixQuantite)$ ")
Text("Total taxe:\(facture.calculeTvaC)$ ")

Core data not triggering immediate refresh with SwiftUI

I'm at a loss with this problem I've been troubleshooting the past few days.
I am drawing a custom shape, and the user can move one of the points around via a drag gesture. I want the shape to redraw, live, when moving the point around.
Here is some sample code that is working using CGPoint.
import SwiftUI
struct ShapeTest: Shape {
#State var points: [CGPoint]
#State var closed: Bool = true
func path(in rect: CGRect) -> Path {
var path = Path()
if (points.count > 0) {
path.move(to: points.first!)
path.addLines(points)
if closed { path.closeSubpath() }
}
return path
}
}
struct RedrawEdgeTestCGPoint: View {
#State var points = [CGPoint]()
#State var originalPosition: CGPoint? = nil
private var movePointDragGesture: some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged { value in
if points.isEmpty {
return
}
if originalPosition == nil {
originalPosition = CGPoint(x: points.last!.x, y: points.last!.y)
}
let lastIndex = points.count - 1
points[lastIndex].x = originalPosition!.x + value.translation.width
points[lastIndex].y = originalPosition!.y + value.translation.height
}
.onEnded { value in
originalPosition = nil
}
}
private var addNewPointGesture: some Gesture {
TapGesture(count: 2)
.onEnded {
points.append(CGPoint())
}
}
var body: some View {
GeometryReader { geometry in
Group {
ShapeTest(points: points)
.stroke()
}
.contentShape(Rectangle())
.gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
}
}
}
struct RedrawEdgeTestCGPoint_Previews: PreviewProvider {
static var previews: some View {
RedrawEdgeTestCGPoint()
}
}
Now, here is another code sample. This example is generally the same thing, but this is using a Point entity I've defined in Core Data instead of CGPoint.
import SwiftUI
struct RedrawEdgeTestCoreData: View {
#Environment(\.managedObjectContext) private var viewContext
#State var points: [Point] = []
#State var originalPosition: CGPoint? = nil
private var movePointDragGesture: some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged { value in
if points.isEmpty {
return
}
if originalPosition == nil {
originalPosition = CGPoint(x: points.last!.x, y: points.last!.y)
}
let lastIndex = points.count - 1
points[lastIndex].x = originalPosition!.x + value.translation.width
points[lastIndex].y = originalPosition!.y + value.translation.height
}
.onEnded { value in
originalPosition = nil
}
}
private var addNewPointGesture: some Gesture {
TapGesture(count: 2)
.onEnded {
points.append(Point(context: viewContext))
}
}
var body: some View {
GeometryReader { geometry in
Group {
ShapeTest(points: points.map { CGPoint(x: $0.x, y: $0.y) })
.stroke()
}
.contentShape(Rectangle())
.gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
}
}
}
struct RedrawEdgeTestCoreData_Previews: PreviewProvider {
static var previews: some View {
RedrawEdgeTestCoreData()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
The version utilizing Core Data does not update while dragging. It will redraw when I double tap to add another point.
Any suggestions as to why this is happening?
I used the approach suggested by jnpdx and it works.
import SwiftUI
class PointsViewModel: ObservableObject {
#Published var points = [Point]()
func movePoint(index: Int, x: CGFloat, y: CGFloat) {
points[index].x = x
points[index].y = y
objectWillChange.send()
}
}
struct RedrawEdgeTestCoreData: View {
#Environment(\.managedObjectContext) private var viewContext
#ObservedObject var viewModel: PointsViewModel
#State var originalPosition: CGPoint? = nil
private var movePointDragGesture: some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged { value in
if viewModel.points.isEmpty {
return
}
if originalPosition == nil {
originalPosition = CGPoint(x: viewModel.points.last!.x, y: viewModel.points.last!.y)
}
let lastIndex = viewModel.points.count - 1
viewModel.movePoint(index: lastIndex, x: originalPosition!.x + value.translation.width, y: originalPosition!.y + value.translation.height)
}
.onEnded { value in
originalPosition = nil
}
}
private var addNewPointGesture: some Gesture {
TapGesture(count: 2)
.onEnded {
viewModel.points.append(Point(context: viewContext))
}
}
var body: some View {
GeometryReader { geometry in
ZStack {
ShapeTest(points: viewModel.points.map { CGPoint(x: $0.x, y: $0.y) })
.stroke()
}
.contentShape(Rectangle())
.gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
}
}
}
struct RedrawEdgeTestCoreData_Previews: PreviewProvider {
static var previews: some View {
RedrawEdgeTestCoreData(viewModel: PointsViewModel())
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

Convert address to coordinates using MKLocalSearchCompleter and CoreLocation

I have tried to make an app with a textfield to let user input a location, using MKLocalSearchCompleter to complete the searching. After that i would like to get the coordinate and display on the MapKit. However, I failed to get the coordinate using the Geocoder.
class LocationSearchService: NSObject, ObservableObject, MKLocalSearchCompleterDelegate {
#Published var searchQuery = ""
var completer: MKLocalSearchCompleter
#Published var completions: [MKLocalSearchCompletion] = []
var cancellable: AnyCancellable?
override init() {
completer = MKLocalSearchCompleter()
super.init()
cancellable = $searchQuery.assign(to: \.queryFragment, on: self.completer)
completer.delegate = self
}
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
self.completions = completer.results
}
}
The location manager as follows:
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
private let geocoder = CLGeocoder()
let objectWillChange = PassthroughSubject<Void, Never>()
#Published var status: CLAuthorizationStatus? {
willSet { objectWillChange.send() }
}
#Published var location: CLLocation? {
willSet { objectWillChange.send() }
}
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
#Published var placemark: CLPlacemark? {
willSet { objectWillChange.send() }
}
private func lookupLocation() {
guard let location = self.location else { return }
geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
if error == nil {
self.placemark = places?[0]
} else {
self.placemark = nil
}
})
}
// !!! This is the function I would like to use to get the Coordinate from the address obtained from LocationSearchService
func getCoordinate(address: String) {
geocoder.geocodeAddressString(address, completionHandler: { (places, error) in
if error == nil {
self.placemark = places?[0]
self.location = self.placemark?.location
} else {
self.placemark = nil
self.location = nil
}
})
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.status = status
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return } //.first or .last?
self.location = location
self.lookupLocation()
}
}
Content View like this:
struct ContentView: View {
#State private var location: String = ""
#ObservedObject var lm = LocationManager()
private let completer = MKLocalSearchCompleter()
#ObservedObject var locationSearchService = LocationSearchService()
var body: some View {
NavigationView {
VStack {
AddressSearchBar(text: $locationSearchService.searchQuery)
List(locationSearchService.completions, id: \.self) { completion in
VStack(alignment: .leading) {
Text(completion.title)
// Error here, I cannot translate the address to location
//Text(lm.getCoordinate(address: completion.title))
}
}.navigationTitle("Search Location")
}
}
A few issues here:
I would like to convert the user selected item (which I failed to implement here) to the address (completion.title) -- i.e., need to get user selection on the suggested item.
I would like to convert the address found in the suggestion to a coordinate, so that I can mark on MapView.

SwiftUI and dynamic NSSortDescriptors in #FetchRequest

I have a list with items that contain a title and a date. User can set what to sort on (title or date).
However I can't figure out how to change the NSSortDescriptor dynamically.
import SwiftUI
import CoreData
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Test.title, ascending: true)], animation: .default) private var items: FetchedResults<Test>
#State private var sortType: Int = 0
#State private var sortDescriptor: NSSortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
var body: some View {
Picker(selection: $sortType, label: Text("Sort")) {
Text("Title").tag(0)
Text("Date").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: sortType) { value in
sortType = value
if sortType == 0 {
sortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
} else {
sortDescriptor = NSSortDescriptor(keyPath: \Test.date, ascending: true)
}
}
List {
ForEach(items) { item in
let dateString = itemFormatter.string(from: item.date!)
HStack {
Text(item.title!)
Spacer()
Text(dateString)
}
}
}
.onAppear(perform: {
if items.isEmpty {
let newEntry1 = Test(context: self.viewContext)
newEntry1.title = "Apple"
newEntry1.date = Date(timeIntervalSince1970: 197200800)
let newEntry2 = Test(context: self.viewContext)
newEntry2.title = "Microsoft"
newEntry2.date = Date(timeIntervalSince1970: 168429600)
let newEntry3 = Test(context: self.viewContext)
newEntry3.title = "Google"
newEntry3.date = Date(timeIntervalSince1970: 904903200)
let newEntry4 = Test(context: self.viewContext)
newEntry4.title = "Amazon"
newEntry4.date = Date(timeIntervalSince1970: 773402400)
if self.viewContext.hasChanges {
try? self.viewContext.save()
}
}
})
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
return formatter
}()
When I change
#FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Test.title, ascending: true)], animation: .default) private var items: FetchedResults<Test>
to
#FetchRequest(sortDescriptors: [sortDescriptor], animation: .default) private var items: FetchedResults<Test>
the error "Cannot use instance member 'sortDescriptor' within property initializer; property initializers run before 'self' is available" appears.
I also tried to store the NSSortDescriptor in a UserDefault and create an init that creates it's own FetchRequest.. still no dynamic sorting...
Anyone a pointer where to look to solve this problem?
Whole project found here: https://github.com/l1ghthouse/FRDSD
Solved! Thanks to Asperi pointing to this QA: https://stackoverflow.com/a/59345830/12299030
import SwiftUI
import CoreData
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#AppStorage("firstLaunch") var firstLaunch: Bool = true
#State var sortDescriptor: NSSortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
#State private var sortType: Int = 0
var body: some View {
Picker(selection: $sortType, label: Text("Sort")) {
Text("Title").tag(0)
Text("Date").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: sortType) { value in
sortType = value
if sortType == 0 {
sortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
} else {
sortDescriptor = NSSortDescriptor(keyPath: \Test.date, ascending: true)
}
}
ListView(sortDescripter: sortDescriptor)
.onAppear(perform: {
if firstLaunch == true {
let newEntry1 = Test(context: self.viewContext)
newEntry1.title = "Apple"
newEntry1.date = Date(timeIntervalSince1970: 197200800)
let newEntry2 = Test(context: self.viewContext)
newEntry2.title = "Microsoft"
newEntry2.date = Date(timeIntervalSince1970: 168429600)
let newEntry3 = Test(context: self.viewContext)
newEntry3.title = "Google"
newEntry3.date = Date(timeIntervalSince1970: 904903200)
let newEntry4 = Test(context: self.viewContext)
newEntry4.title = "Amazon"
newEntry4.date = Date(timeIntervalSince1970: 773402400)
if self.viewContext.hasChanges {
try? self.viewContext.save()
}
firstLaunch = false
}
})
}
}
struct ListView: View {
#FetchRequest var items: FetchedResults<Test>
#Environment(\.managedObjectContext) var viewContext
init(sortDescripter: NSSortDescriptor) {
let request: NSFetchRequest<Test> = Test.fetchRequest()
request.sortDescriptors = [sortDescripter]
_items = FetchRequest<Test>(fetchRequest: request)
}
var body: some View {
List {
ForEach(items) { item in
let dateString = itemFormatter.string(from: item.date!)
HStack {
Text(item.title!)
Spacer()
Text(dateString)
}
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
return formatter
}()

NaN is schown when a calculation has no value inside. How can "0" or "-" be displayed instead in SwiftUI

If a calculation is performed in SwiftUI and no values are added to the Textfield it displays "-NaN" (Not a Nummber?) instead.
How can a value (for example "0", "-") be displayed until values are inserted?
I have tried adding #State private var display = 0 but it displayed both values...
Example code:
struct ContentView: View {
#EnvironmentObject var userData: UserData
var Brutto: Double{
let price = Double(userData.BPrice) ?? 0
let rent = Double(userData.Rent) ?? 0
let Brutto = rent * 12 / price * 100
return Brutto
}
var body: some View {
VStack {
Text ("\(Brutto, specifier: "%.1f")")
}
// The UserData File:
class UserData : ObservableObject {
private static let userDefaultBPrice = "BPrice"
private static let userDefaultRent = "Rent"
#Published var BuyingPrice = UserDefaults.standard.string(forKey: UserData.userDefaultBPrice) ?? ""
{
didSet {
UserDefaults.standard.set(self.BPrice, forKey: UserData.userDefaultBPrice)
}
}
#Published var Rent = UserDefaults.standard.string(forKey: UserData.userDefaultRent) ?? "" {
didSet {
UserDefaults.standard.set(self.Rent, forKey: UserData.userDefaultRent)
}
}
private var canc: AnyCancellable!
}
Thanks
Here is possible approach
VStack {
Text ("\(Brutto.isNaN ? 0 : Brutto, specifier: "%.1f")")
}

Resources