multiply two value from textfield by using core data in swiftui - core-data

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)$ ")

Related

Tappable portions within a string for SwiftUI

I'm trying to add tappable segments to a Text and I have most of it but I cannot make an array of Texts with .onTapGestures. How can I add the tap gestures to the Texts.
The goal is to have a string such as "here is a phone: xxx-xxx-xxxx and here is a link: xxx.com. Go for it." and have xxx-xx-xxx and xxx.com tappable.
#available(iOS 14, *)
struct ConversationText: View {
var text: String
var connectMessageType: ConnectMessageType
var stringArray = [String]()
var phones = [String]()
var weblinks = [String]()
var textArray = [Text]()
init(text: String, connectMessageType: ConnectMessageType) {
self.text = text
self.connectMessageType = connectMessageType
stringArray = text.components(separatedBy: " ")
phones = phones(from: text) ?? [String]()
weblinks = weblinks(from: text) ?? [String]()
textArray = makeTexts(from: stringArray)
}
var myColor: Color {
connectMessageType == .me ? .secondaryYellow : .primaryBlue
}
var body: some View {
textArray.reduce(Text(""), { $0 + $1 })
}
func phones(from text: String) -> [String]? {
Tappables().getPhoneNumbers(from: self.text)
}
func weblinks(from text: String) -> [String]? {
Tappables().getWebLinks(from: self.text)
}
func makeTexts(from stringArray: [String]) -> [Text] {
var textArray = [Text]()
for string in stringArray {
if phones.contains(string) {
let text = Text(string+" ")
.foregroundColor(myColor)
// .onTapGesture {
// startCall(number: string, user: nil)
// }
textArray.append(text)
} else if weblinks.contains(string) {
let text = Text(string+" ")
.foregroundColor(myColor)
// .onTapGesture {
// if let url = URL(string: string),
// UIApplication.shared.canOpenURL(url){
// UIApplication.shared.open(url)
// }
textArray.append(text)
} else {
textArray.append(Text(string+" "))
}
}
return textArray
}
}```
Here is possible approach - use string formatted by Markdown.
Tested with Xcode 13.3 / iOS 15.4
A main part:
Text("here is a phone: [xxx-xxx-xxxx](tel:xxx-xxx-xxxx) and here is a link: [xxx.com](asperi://apple.com). Go for it.")
.onOpenURL {
print(">> got: \($0)")
}
Complete findings & code

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

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()
}
}

SwifUI: How to get manual list to refresh when updating CoreData records?

I have a CoreData table that has several repeated records that don't need to be stored but do need to be displayed in my user interface. I have manually created my arrays based on the CoreData table. I have made them Observable Objects so they should automatically update and I have made them Hashable and Equatable.
My problem is that the list does not update when the database records are updated. This works fine when records are being added.
Here is my much simplified code in full that demonstrates the problem:
import SwiftUI
let persistentContainerQueue = OperationQueue()
let firstNames = ["Michael", "Damon", "Jacques", "Mika", "Fernando", "Kimi", "Lewis", "Jenson", "Sebastion", "Nico"]
let lastNames = ["Schumacher", "Hill", "Villeneuve", "Hakkinen", "Alonso", "Raikkonen", "Hamilton", "Button", "Vettel", "Rosberg"]
class RepeatedPerson: ObservableObject, Hashable
{
#Published var person: Person
#Published var index: Int
#Published var tested: Bool
init (person: Person, index: Int, tested: Bool)
{
self.person = person
self.index = index
self.tested = tested
}
func hash(into hasher: inout Hasher)
{
hasher.combine(person.firstName)
hasher.combine(person.lastName)
hasher.combine(index)
}
static func == (lhs: RepeatedPerson, rhs: RepeatedPerson) -> Bool
{
return lhs.person.firstName == rhs.person.firstName &&
lhs.person.lastName == rhs.person.lastName &&
lhs.index == rhs.index
}
}
class RepeatedPeople: ObservableObject
{
#Published var people: [RepeatedPerson] = []
}
func getRepeatedPeople() -> [RepeatedPerson]
{
var repeatedPeople:[RepeatedPerson] = []
let records = allRecords(Person.self)
for person in records
{
for index in 1...3
{
repeatedPeople.append(RepeatedPerson(person: person, index: index, tested: true))
}
}
return repeatedPeople
}
struct ContentView: View
{
#Environment(\.managedObjectContext) private var viewContext
#ObservedObject var repeatedPeople = RepeatedPeople()
init()
{
repeatedPeople.people = getRepeatedPeople()
}
var body: some View
{
VStack
{
List()
{
ForEach(repeatedPeople.people, id: \.self)
{ repeatedPerson in
Text("\(repeatedPerson.index)) \(repeatedPerson.person.firstName!) \(repeatedPerson.person.lastName!)")
}
}
HStack
{
Button("Add Record", action:
{
addItem()
repeatedPeople.people = getRepeatedPeople()
})
Button("Change Record", action:
{
let q = allRecords(Person.self)
let oldLastName = q[0].lastName
q[0].lastName = lastNames.randomElement()!
print ("changed \(q[0].firstName!) \(oldLastName!) -> \(q[0].firstName!) \(q[0].lastName!)")
saveDatabase()
})
Button("Reset Database", action:
{
deleteAllRecords(Person.self)
})
}
}
}
private func addItem()
{
withAnimation
{
let newItem = Person(context: viewContext)
newItem.timestamp = Date()
newItem.firstName = firstNames.randomElement()!
newItem.lastName = lastNames.randomElement()!
print ("added \(newItem.firstName!) \(newItem.lastName!)")
saveDatabase()
}
}
}
func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
let context = PersistenceController.shared.container.viewContext
let request = T.fetchRequest()
if let sortDescriptor = sort
{
request.sortDescriptors = [sortDescriptor]
}
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}
func deleteAllRecords<T: NSManagedObject>(_ type : T.Type)
{
let context = PersistenceController.shared.container.viewContext
let results = allRecords(T.self)
for record in results
{
context.delete(record as NSManagedObject)
}
saveDatabase()
}
func saveDatabase()
{
persistentContainerQueue.addOperation()
{
let context = PersistenceController.shared.container.viewContext
context.performAndWait
{
try? context.save()
}
}
}
To reproduce the problem, add a few records. These will be shown in the list. Then click the 'Update Record' button. The CoreData record will be updated (you can see this the next time you run the app) but the changes will not be shown.
How do I get the new changes to show?
If you add another record the changes will then be shown. A side effect is that the list introduces wild spaces between the records. I have seen this is in other places. Is this a SwiftUI bug?
OK it turned out to be really quite simple. All I actually had to do was remove some of the #Published and provide a UUID for the repeatedPerson record (and for == and hash).
import SwiftUI
import CoreData
let persistentContainerQueue = OperationQueue()
let firstNames = ["Michael", "Damon", "Jacques", "Mika", "Fernando", "Kimi", "Lewis", "Jenson", "Sebastion", "Nico"]
let lastNames = ["Schumacher", "Hill", "Villeneuve", "Hakkinen", "Alonso", "Raikkonen", "Hamilton", "Button", "Vettel", "Rosberg"]
class RepeatedPerson: ObservableObject, Hashable
{
var id: UUID = UUID()
var index: Int
var person: Person?
init (person: Person, index: Int)
{
self.person = person
self.index = index
}
func hash(into hasher: inout Hasher)
{
hasher.combine(id)
}
static func == (lhs: RepeatedPerson, rhs: RepeatedPerson) -> Bool
{
return lhs.id == rhs.id
}
}
class RepeatedPeople: ObservableObject
{
#Published var people: [RepeatedPerson] = []
}
func getRepeatedPeople() -> [RepeatedPerson]
{
var repeatedPeople:[RepeatedPerson] = []
let records = allRecords(Person.self)
for person in records
{
for index in 1...3
{
repeatedPeople.append(RepeatedPerson(person: person, index: index))
}
}
return repeatedPeople
}
struct ContentView: View
{
#Environment(\.managedObjectContext) private var viewContext
#ObservedObject var repeatedPeople = RepeatedPeople()
init()
{
repeatedPeople.people = getRepeatedPeople()
}
var body: some View
{
VStack
{
List()
{
ForEach(repeatedPeople.people, id: \.self)
{ repeatedPerson in
Text("\(repeatedPerson.index)) \(repeatedPerson.person!.firstName!) \(repeatedPerson.person!.lastName!)")
}
}
HStack
{
Button("Add Record", action:
{
addItem()
repeatedPeople.people = getRepeatedPeople()
})
Button("Change Record", action:
{
let q = allRecords(Person.self)
let r = q.randomElement()!
let oldLastName = r.lastName
r.lastName = lastNames.randomElement()!
print ("changed \(r.firstName!) \(oldLastName!) -> \(r.firstName!) \(r.lastName!)")
saveDatabase()
repeatedPeople.people = getRepeatedPeople()
})
Button("Reset Database", action:
{
print ("Reset database")
deleteAllRecords(Person.self)
repeatedPeople.people = getRepeatedPeople()
})
}
}
}
private func addItem()
{
withAnimation
{
let newItem = Person(context: viewContext)
newItem.timestamp = Date()
newItem.firstName = firstNames.randomElement()!
newItem.lastName = lastNames.randomElement()!
print ("added \(newItem.firstName!) \(newItem.lastName!)")
saveDatabase()
}
}
}
func query<T: NSManagedObject>(_ type : T.Type, predicate: NSPredicate? = nil, sort: NSSortDescriptor? = nil) -> [T]
{
let context = PersistenceController.shared.container.viewContext
let request = T.fetchRequest()
if let sortDescriptor = sort
{
request.sortDescriptors = [sortDescriptor]
}
if let predicate = predicate
{
request.predicate = predicate
}
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}
func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
return query(T.self, sort: sort)
}
func deleteAllRecords<T: NSManagedObject>(_ type : T.Type)
{
let context = PersistenceController.shared.container.viewContext
let results = allRecords(T.self)
for record in results
{
context.delete(record as NSManagedObject)
}
saveDatabase()
}
func saveDatabase()
{
persistentContainerQueue.addOperation()
{
let context = PersistenceController.shared.container.viewContext
context.performAndWait
{
try? context.save()
}
}
}

Highlight only one word of a string [duplicate]

Hello I'm new to Swift and am using SwiftUI for my project where I download some weather data and I display it in the ContentView().
I would like to highlight some part of the Text if it contains some specific word, but I don't have any idea how to start.
In ContentView(), I have tried to set a function receiving the string downloaded from web and return a string. I believe this is wrong, because SwiftUI does not apply the modifiers at the all for the Text.
For example, in my ContentView() I would like the word thunderstorm to have the .bold() modifier:
struct ContentView: View {
let testo : String = "There is a thunderstorm in the area"
var body: some View {
Text(highlight(str: testo))
}
func highlight(str: String) -> String {
let textToSearch = "thunderstorm"
var result = ""
if str.contains(textToSearch) {
let index = str.startIndex
result = String( str[index])
}
return result
}
}
If that requires just simple word styling then here is possible solution.
Tested with Xcode 11.4 / iOS 13.4
struct ContentView: View {
let testo : String = "There is a thunderstorm in the area. Added some testing long text to demo that wrapping works correctly!"
var body: some View {
hilightedText(str: testo, searched: "thunderstorm")
.multilineTextAlignment(.leading)
}
func hilightedText(str: String, searched: String) -> Text {
guard !str.isEmpty && !searched.isEmpty else { return Text(str) }
var result: Text!
let parts = str.components(separatedBy: searched)
for i in parts.indices {
result = (result == nil ? Text(parts[i]) : result + Text(parts[i]))
if i != parts.count - 1 {
result = result + Text(searched).bold()
}
}
return result ?? Text(str)
}
}
Note: below is previously used function, but as commented by #Lkabo it has limitations on very long strings
func hilightedText(str: String) -> Text {
let textToSearch = "thunderstorm"
var result: Text!
for word in str.split(separator: " ") {
var text = Text(word)
if word == textToSearch {
text = text.bold()
}
result = (result == nil ? text : result + Text(" ") + text)
}
return result ?? Text(str)
}
iOS 13, Swift 5. There is a generic solution described in this medium article. Using it you can highlight any text anywhere with the only catch being it cannot be more then 64 characters in length, since it using bitwise masks.
https://medium.com/#marklucking/an-interesting-challenge-with-swiftui-9ebb26e77376
This is the basic code in the article.
ForEach((0 ..< letter.count), id: \.self) { column in
Text(letter[column])
.foregroundColor(colorCode(gate: Int(self.gate), no: column) ? Color.black: Color.red)
.font(Fonts.futuraCondensedMedium(size: fontSize))
}
And this one to mask the text...
func colorCode(gate:Int, no:Int) -> Bool {
let bgr = String(gate, radix:2).pad(with: "0", toLength: 16)
let bcr = String(no, radix:2).pad(with: "0", toLength: 16)
let binaryColumn = 1 << no - 1
let value = UInt64(gate) & UInt64(binaryColumn)
let vr = String(value, radix:2).pad(with: "0", toLength: 16)
print("bg ",bgr," bc ",bcr,vr)
return value > 0 ? true:false
}
You can concatenate with multiple Text Views.
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View{
let testo : String = "There is a thunderstorm in the area"
let stringArray = testo.components(separatedBy: " ")
let stringToTextView = stringArray.reduce(Text(""), {
if $1 == "thunderstorm" {
return $0 + Text($1).bold() + Text(" ")
} else {
return $0 + Text($1) + Text(" ")
}
})
return stringToTextView
}
}
PlaygroundPage.current.setLiveView(ContentView())
If you are targeting iOS15 / macOS12 and above, you can use AttributedString. For example:
private struct HighlightedText: View {
let text: String
let highlighted: String
var body: some View {
Text(attributedString)
}
private var attributedString: AttributedString {
var attributedString = AttributedString(text)
if let range = attributedString.range(of: highlighted)) {
attributedString[range].backgroundColor = .yellow
}
return attributedString
}
}
If you want your match to be case insensitive, you could replace the line
if let range = attributedString.range(of: highlighted)
with
if let range = AttributedString(text.lowercased()).range(of: highlighted.lowercased())
The answer of #Asperi works well. Here is a modified variant with a search by array of single words:
func highlightedText(str: String, searched: [String]) -> Text {
guard !str.isEmpty && !searched.isEmpty else { return Text(str) }
var result: Text!
let parts = str.components(separatedBy: " ")
for part_index in parts.indices {
result = (result == nil ? Text("") : result + Text(" "))
if searched.contains(parts[part_index].trimmingCharacters(in: .punctuationCharacters)) {
result = result + Text(parts[part_index])
.bold()
.foregroundColor(.red)
}
else {
result = result + Text(parts[part_index])
}
}
return result ?? Text(str)
}
Usage example:
let str: String = "There is a thunderstorm in the area. Added some testing long text to demo that wrapping works correctly!"
let searched: [String] = ["thunderstorm", "wrapping"]
highlightedText(str: str, searched: searched)
.padding()
.background(.yellow)
You can also make AttributedString with markdown this way
do {
return try AttributedString(markdown: foreignSentence.replacing(word.foreign, with: "**\(word.foreign)**"))
} catch {
return AttributedString(foreignSentence)
}
and just use Text
Text(foreignSentenceMarkdown)

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