In the older version of swift 3 I put my variable at the after the import statetments. With the new Swiftui is this still a good way.
1) Swift 3
import UIKit
import AVFoundation
class pushgroups_vc: BaseViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UITextViewDelegate
{
var dbarray_id: [String] = []
var dbarray_code: [String] = []
var dbarray_title: [String] = []
var dbarray_description: [String] = []
var dbarray_public: [String] = []
var dbarray_accesscode: [String] = []
2) Is this still valid
in Swift 5 xcode 11
import SwiftUI
import Combine
let newVars = commonfunc()
var devicetoken:String = ""
var email:String = ""
var password:String = ""
var dbarray_id: [String] = []
var dbarray_subject: [String] = []
var dbarray_body: [String] = []
var dbarray_image: [String] = []
var dbarray_location: [String] = []
var dbarray_start_date: [String] = []
Related
I am trying to use fileExport logic to copy history data from CoreData to a CSV file. Since the data is coming from CoreData I need to use #FetchRequest and it is my understanding that #FetchRequest may only be used in a view.
I'm getting a number of errors related to misusing a view and transferring the data to fileExporter. It seems like I'm misusing a view to transfer data. Are there other features of CoreData that can be used to retrieve data outside of a view?
I have several similar structures that create CSV files without using coreData working. Therefore I believe my structures CreateHistoryTable and MessageDocument are working correctly. So I need help getting my data from CoreData to fileExporter.
struct CreateHistoryTable: View {
#Environment(\.managedObjectContext) var viewContext
#State private var showingExporter: Bool = false
#State private var document: MessageDocument?
var body: some View {
VStack {
Button ( action: {
self.showingExporter = true
document = CreateHistoryCSV() <-- need help here retrieving document to export
}) {
HStack (alignment: .firstTextBaseline) {
Text("Export History Entries")
.fontWeight(.bold)
.font(.title3)
Image(systemName: "square.and.arrow.up")
}
}
}.fileExporter(
isPresented: $showingExporter,
document: document,
contentType: .plainText,
defaultFilename: "TripSenseHistory.csv"
) { result in
switch result {
case .success(let url):
print("Saved to \(url)")
case .failure(let error):
print(error.localizedDescription)
}
}
.navigationBarTitle(Text("Export History Entries"), displayMode: .inline)
}
}
Retrieve data from CoreData and copy to single text string
struct CreateHistoryCSV: View {
#Binding MessageDocument
var csvData: String = ""
var title = ",Trip Sense History Entries,\n"
var subtitle = "Date,Category,Payment Type, Amount\n"
var messageRow: String = ""
var sHisCatName: String = ""
var sHisDsc: String = ""
var sHisPayType: String = ""
var sHisMoney: String = ""
var dHisMoney: Double = 0.0
var sHisLoc: String = ""
var payType = ["Cash", "Debit", "Credit"]
var code: String = ""
var messageRow = ""
// fetch core data
#FetchRequest(
entity: CurrTrans.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \CurrTrans.entryDT, ascending: true)]
) var currTrans: FetchedResults<CurrTrans>
var body: some View {
var csvData = title + subtitle
for item in 0..<currTrans.count {
let messageRow = createHistoryRow(item: item)
csvData += messageRow
}
print(csvData)
//return MessageDocument(message: csvData)
}
func createHistoryRow(item: Int) ->(String) {
// format expense date and time
let dHisDate = currTrans[item].entryDT ?? Date()
let sHisDate = dHisDate.formatted(.dateTime.year().day().month(.wide).hour().minute())
// get history category
let sHisCatName = currTrans[item].entryCatName ?? "cat name"
// get payment type
let sHisPayType = payType[Int(currTrans[item].entryPT)]
// get description
let sHisDsc = currTrans[item].entryDsc ?? "Unk"
// format transaction amount
let code = currTrans[item].entryCode ?? "Unk" // 3 digit country code for this transaction
let dHisMoney = currTrans[item].entryMoney
let sHisMoney = dHisMoney.formatted(.currency(code: sym))
// get location
let sHisLoc = currTrans[item].entryAddr ?? "Unk"
messageRow = "\"\(sHisDate)\"" + "," + sHisCatName + "," + sHisPayType + "," + "\"\(sHisDsc)\"" + "," + "\"\(sHisMoney)\"" + "," + "\"\(sHisLoc)\"" + "\n"
return messageRow
}
}
This code is part of the Swiftui file export logic
struct MessageDocument: FileDocument {
static var readableContentTypes: [UTType] { [.plainText] }
var message: String = ""
init(message: String) {
self.message = message
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let string = String(data: data, encoding: .utf8)
else {
throw CocoaError(.fileReadCorruptFile)
}
message = string
}
// this will be called when the system wants to write our data to disk
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
return FileWrapper(regularFileWithContents: message.data(using: .utf8)!)
}
}
With further research I realized that I could place the #FetchRequest in CreateHistoryTable along with the fileExporter view logic. That allowed me to change CreateHistoryCSV to a function of CreateHistoryTable. No changes were made to createHistoryRow
// copy history entrys to csv file
struct CreateHistoryTable: View {
#EnvironmentObject var base: BaseCurrency
#EnvironmentObject var bank: BankWithdrawal
#EnvironmentObject var userData: UserData
#Environment(\.managedObjectContext) var viewContext
#State private var showingExporter: Bool = false
#State private var document: MessageDocument?
var title = ",Trip Sense History Entries,\n"
var subtitle = "Date,Category,Payment Type, Amount\n"
var messageRow: String = ""
var sHisCatName: String = ""
var sHisDsc: String = ""
var sHisPayType: String = ""
var sHisMoney: String = ""
var dHisMoney: Double = 0.0
var sHisLoc: String = ""
var payType = ["Cash", "Debit", "Credit"]
var sym: String = ""
// fetch core data
#FetchRequest(
entity: CurrTrans.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \CurrTrans.entryDT, ascending: true)]
) var currTrans: FetchedResults<CurrTrans>
var body: some View {
VStack {
Button ( action: {
self.showingExporter = true
let dates = userData.formatCsvDate(startDate: startDate, endDate: endDate)
document = CreateHistoryCSV(dates: dates)
}) {
HStack (alignment: .firstTextBaseline) {
Text("Export History Entries")
.fontWeight(.bold)
.font(.title3)
Image(systemName: "square.and.arrow.up")
}
}
}.fileExporter(
isPresented: $showingExporter,
document: document,
contentType: .plainText,
defaultFilename: "TripSenseHistory.csv"
) { result in
switch result {
case .success(let url):
print("Saved to \(url)")
case .failure(let error):
print(error.localizedDescription)
}
}
.navigationBarTitle(Text("Export History Entries"), displayMode: .inline)
}
func CreateHistoryCSV() -> (MessageDocument) {
var csvData = title + subtitle
for item in 0..<currTrans.count {
let messageRow = createHistoryRow(item: item)
csvData += messageRow
}
print(csvData)
return MessageDocument(message: csvData)
}
I am using an #EnvironmentObject (which is the ViewModel) and I have a demoData() function.
When I press it the data does change but my View is not updated.
How do I get the data to change in the View?
Thank you.
The view information:
import Combine
import SwiftUI
struct MainView: View {
#EnvironmentObject var entry:EntryViewModel
var body: some View {
TextField("Beg Value", text: self.$entry.data.beg)
TextField("Beg Value", text: self.$entry.data.end)
Button(action: { self.entry.demoData() }) { Text("Demo Data") }
}
}
ViewModel:
class EntryViewModel: ObservableObject {
#Published var data:EntryData = EntryData()
func demoData() {
var x = Int.random(in: 100000..<120000)
x = Int((Double(x)/100).rounded()*100)
data.beg = x.withCommas()
x = Int.random(in: 100000..<120000)
x = Int((Double(x)/100).rounded()*100)
data.end = x.withCommas()
}
Model:
EntryData:ObservableObject {
#Published var beg:String = ""
#Published var end:String = ""
}
This is because EntryData is a class and if you change its properties it will still be the same object.
This #Published will only fire when you reassign the data property:
#Published var data: EntryData = EntryData()
A possible solution is to use a simple struct instead of an ObservableObject class:
struct EntryData {
var beg: String = ""
var end: String = ""
}
When a struct is changed, it's copied and therefore #Published will send objectWillChange.
I'm new to IOS and SwiftUI coding. I googled a lot but could not find a solution, how to pass a computed variable to the next view.
Here snippets of what I have:
import SwiftUI
struct ContentView: View {
#State private var isShowingResultView = false
#State private var netRate = "0"
#State var daysMonth = "0"
#State var hoursWeek: String = "0"
#State var daysOnsite: String = "0"
#State var ticketCost: String = "0"
#State var hotelCost: String = "0"
#State var otherCost: String = "0"
//#State var travellCostResult: Double = 0.00
var travellCostPerHour: Double{
get {
let daysMonthNbr = Int(daysMonth) ?? 0
let hoursWeekNbr = Int(hoursWeek) ?? 0
let daysOnsiteNbr = Int(daysOnsite) ?? 0
let ticketCostNbr = Double(ticketCost) ?? 0
let hotelCostNbr = Double(hotelCost) ?? 0
let otherCostNbr = Double(otherCost) ?? 0
let travellCostPerWeek = (ticketCostNbr + (Double((daysOnsiteNbr-1))*hotelCostNbr)+otherCostNbr)
let travellCostPerHour: Double = Double(travellCostPerWeek) / Double(hoursWeekNbr)
return travellCostPerHour.isNaN ? 0 : travellCostPerHour
}
}
.
.
.
var body: some View {
HStack {
NavigationLink("Calculate", destination: ResultView(netRate: self.$netRate, travellCostPerHour: travellCostPerHour), isActive: $isShowingResultView).navigationBarTitle("Result").buttonStyle(GradientButtonStyle())
.adaptToKeyboard()
}
struct ResultView: View {
#Binding var netRate: String
#Binding var travellCostPerHour: Double
.
.
.
struct ResultView_Previews: PreviewProvider {
#State static var netRate: String = ""
#State static var travellCostPerHour: Double = 0.00
static var previews: some View {
ResultView(netRate: $netRate, travellCostPerHour: $travellCostPerHour )
}
}
I get this error msg in the navigationLink for travellCostPerHour: Cannot convert value of type 'Double' to expected argument type 'Binding'
Can one put me on the right path here please?
If it is computed property then binding is not needed, pass it as-is
struct ResultView: View {
#Binding var netRate: String
var travellCostPerHour: Double // << just regular
// .. other code
I want to save some data from a SwiftUI view to a Model so that I can use these data into another SwiftUI view. However, I came up with some error when I try to call the Model class and pass all the data as parameters. The error says:
Cannot use instance member 'expectedEndDate' within property initializer; property initializers run before 'self' is available"
Here is my SearchBikeDataModel() code:
import Foundation
class SearchBikeDataModel: ObservableObject {
#Published var startDate: Date = Date()
#Published var startTime: Date = Date()
#Published var endDate: Date = Date()
#Published var endTime: Date = Date()
#Published var bikeType: String = ""
#Published var collectionPoint: String = ""
#Published var returnPoint: String = ""
init(selectedStartDate: Date, selectedStartTime: Date, selectedEndDate: Date, selectedEndTime: Date, bikeType: String, collectionPoint: String, returnPoint: String) {
self.startDate = selectedStartDate
self.startTime = selectedStartTime
self.endDate = selectedEndDate
self.endTime = selectedEndTime
self.bikeType = bikeType
self.collectionPoint = collectionPoint
self.returnPoint = returnPoint
}
}
And here is the code where I try to pass data as parameters:
import SwiftUI
struct BikeSearchFormView: View {
#Binding var isDateTimeShown: Bool
#Binding var isEndDateTimePickerShown: Bool
#State var expectedStartDate = Date()
#State var expectedStartTime = Date()
#State var expectedEndDate = Date()
#State var expectedEndTime = Date()
#State var isBikeTypePickerExpand: Bool = false
#State var isDropOffPointPickerExpand: Bool = false
#State var isPickUpPointPickerExpand: Bool = false
#State var selectedBikeType: String = "BIKE TYPE"
#State var selectedDropOffPoint: String = "DROP OFF POINT"
#State var selectedPickUpPoint: String = "PICKUP POINT"
#State var findBikeError: String = ""
#State var isActive: Bool = false
#ObservedObject var bikeTypeViewModel = VehicleTypeViewModel()
#ObservedObject var findBikeViewModel = FindBikeViewModel()
#ObservedObject var dataModel = SearchBikeDataModel(selectedStartDate: expectedStartDate, selectedStartTime: expectedStartTime, selectedEndDate: expectedEndDate, selectedEndTime: expectedEndTime, bikeType: selectedBikeType, collectionPoint: selectedPickUpPoint, returnPoint: selectedDropOffPoint)
var body: some View {
Text("Hello, World")
}
}
I have omitted codes of my UI as the question is just about am I following the right way to pass the data into parameters.
So I have been trying to fix this problem that has been already discussed here for a few times but I cannot seem to truly understand where the issue comes from and how to fix it in my application. Apologies if this one is obvious but I've picked up SwiftUI a week ago.
Basically what I am doing here is that I have a function called countStrokes() where I have an array of strings as an input. First of all I convert the array to an int array, then compute the sum of the array and return the sum as a String.
After that I declare a new lazy var called strokes and initialise it by calling the countStrokes() function.
All I want to do in the View is to print out the strokes value using the Text() module.
Any ideas of how to modify my existing code will be much appreciated.
import SwiftUI
struct Skore: View {
#State var skore: [String]
lazy var strokes: String = countStrokes(array: skore)
var body: some View {
Text(strokes)
}
}
func countStrokes(array: [String]) -> String {
let newArray = array.compactMap{Int($0)}
let total = newArray.reduce(0, +)
let totalString = String(total)
return totalString
}
The simplest is just use function inline. As soon as your view depends on skore state and countStrokes depends on it, once skore will be modified the corresponding Text will be recalculated and shows correct result.
struct Skore: View {
#State var skore: [String]
var body: some View {
Text(countStrokes(array: skore))
}
}
what you can do is this:
struct Skore: View {
#State var skore: [String]
#State var strokes: String = ""
var body: some View {
Text(strokes).onAppear(perform: loadData)
}
func loadData() {
self.strokes = countStrokes(array: skore)
}
}
func countStrokes(array: [String]) -> String {
let newArray = array.compactMap{Int($0)}
let total = newArray.reduce(0, +)
let totalString = String(total)
return totalString
}
struct ContentView: View {
#State var skore = ["1","2","3"]
var body: some View {
Skore(skore: skore)
}
}