Assigning MyScruct().var = results results in no assignment - swift

Ok.. probably bad title. But here, the problem.
struct DeckView: View {
#State public var results = [ScryfallCard]()
var body: some View {
List(results, id: \.id ) { item in
Mkae a list containing the results.
}.onAppear {
ScryfallData().parseBulkData()
print("Type of results::", type(of: results))
print("results.capacity:", results.capacity)
}
}
}
struct ScryfallData {
func parseBulkData() {
let fm = FileManager.default
let path = Bundle.main.resourcePath
let items = try! fm.contentsOfDirectory(atPath: path!)
var oracleFileName = ""
for fileName in items {
if fileName .hasPrefix("oracle-cards"){
oracleFileName = fileName
}
}
print("if let savedJson = Bundle.main.url")
if let savedJson = Bundle.main.url(forResource: oracleFileName, withExtension: "") {
if let dataOfJson = try? Data(contentsOf: savedJson) {
print("if let dataOfJSON: \(dataOfJson)")
do {
let scryfallDecodeData = try JSONDecoder().decode([ScryfallCard].self, from: dataOfJson)
print("scryfallDecodeData.capacity:", scryfallDecodeData.capacity)
/* error here*/ DeckView().results = scryfallDecodeData
print("DeckView().results: ", DeckView().results)
print("Decoded data:", type(of: scryfallDecodeData))
} catch {
debugPrint("decode failed")
}
}
}
}
}
I keep getting a blank List this in the debugger...
if let dataOfJSON: 73545913 bytes
scryfallDecodeData.capacity: 24391
DeckView().results: []
Decoded data: Array<ScryfallCard>
Type of results:: Array<ScryfallCard>
results.capacity: 0
This means that oiver on the line marked Error Here, I'm asigning the decoded data to the DeckView().results var, but the end result is the data is not getting asigned. Any idea what I'm doing wrong?

You should not be creating a View from view model (ScryfallData), but instead return the decoded data from the parseBulkData function and assign that to results inside the onAppear of your View.
Your models should never know about your UI. Your UI (View in case of SwiftUI) should own the models, not the other way around. This achieves good separation of concerns and also makes your business logic platform and UI agnostic.
struct DeckView: View {
#State public var results = [ScryfallCard]()
var body: some View {
List(results, id: \.id ) { item in
Text(item.text)
}.onAppear {
self.results = ScryfallData().parseBulkData()
}
}
}
struct ScryfallData {
func parseBulkData() -> [ScryfallCard] {
let fm = FileManager.default
let path = Bundle.main.resourcePath
let items = try! fm.contentsOfDirectory(atPath: path!)
var oracleFileName = ""
for fileName in items {
if fileName .hasPrefix("oracle-cards"){
oracleFileName = fileName
}
}
if let savedJson = Bundle.main.url(forResource: oracleFileName, withExtension: "") {
do {
let jsonData = try Data(contentsOf: savedJson)
let scryfallDecodeData = try JSONDecoder().decode([ScryfallCard].self, from: jsonData)
return scryfallDecodeData
} catch {
debugPrint("decode failed")
return []
}
}
return []
}
}

Related

Data from csv file won't show up when called in list on Swift

So I'm learning to import data from csv files to my swift project.
I'm trying to see everything is imported correctly by displaying some of the items in a list. However, even though I'm getting no errors at all, the list doesn't show up.
Can anyone help me out?
My csv setup code:
import Foundation
struct Leden: Identifiable {
var voorNaam: String = ""
var achterNaam: String = ""
var functie: String = ""
var id = UUID()
init (raw: [String]) {
voorNaam = raw[0]
achterNaam = raw[1]
functie = raw[2]
}
}
func loadCSV(from csvName: String) -> [Leden] {
var csvToStruct = [Leden]()
guard let filePath = Bundle.main.path(forResource: csvName, ofType: "csv") else {
return[]
}
var data = ""
do {
data = try String(contentsOfFile: filePath)
} catch {
print(error)
return[]
}
var rows = data.components(separatedBy: "\n")
let columnCount = rows.first?.components(separatedBy: ",").count
rows.removeFirst()
for row in rows {
let csvColumns = row.components(separatedBy: ",")
if csvColumns.count == columnCount {
let ledenStruct = Leden.init(raw: csvColumns)
csvToStruct.append(ledenStruct)
}
}
return csvToStruct
}
My code to make the csv items appear in a list:
struct PraesidiumView: View {
var individu = loadCSV(from: "Ledenlijst")
var body: some View {
NavigationView{
List(individu){Leden in
Text(Leden.voorNaam)
}
.navigationTitle("Praesidium")
}
}
}
try to implement this way, you also learn mvvm way.
class PraesidiumViewModel: ObservableObject {
#Public var ledens: [Leden] = []
func loadCSV() {
// ...
self.ledens = csvToStruct
}
}
struct PraesidiumView: View {
#StateObject var viewModel = PraesidiumViewModel()
var body: some View {
NavigationView {
List(viewModel.ledens){ leden in
Text(leden.voorNaam)
}
.navigationTitle("Praesidium")
}.onAppear() {
viewModel.loadCSV()
}
}
}
you could try something like this (untested) to read your csv data:
func loadCSV(from csvName: String) -> [Leden] {
var csvToStruct = [Leden]()
guard let filePath = Bundle.main.path(forResource: csvName, ofType: "csv") else {
return[]
}
var data = ""
do {
data = try String(contentsOfFile: filePath)
} catch {
print(error)
return []
}
var rows = data.components(separatedBy: "\n")
if let firstRow = rows.first {
rows.removeFirst()
for row in rows {
let csvColumns = row.components(separatedBy: ",")
if csvColumns.count >= 3 {
let ledenCol = Array(csvColumns[0..<3])
let ledenStruct = Leden(raw: ledenCol)
csvToStruct.append(ledenStruct)
}
}
}
return csvToStruct
}
So I find a solution.
I just restarted following the tutorial and this time, I gave all parameters the exact same name as they did in the tutorial and that worked.
So I just messed up because of probably a wrong name assigned to a certain parameter.

Add Data Sorting in TableView with Exception | Swift

Essentially I currently have the following JSON Parse logic in place to group and present the JSON Array in a tableview.
Currently in the fetchJSON function I am grouping person and sorting alphabetically. How can I add logic that makes an exception to this sort and allows person = Jack, to always be on top of the sort and the rest stay alphabetical.
Is there a way to keep the person = "Jack" at the top of the tableview no matter the other tableview data?
Below is my current code:
private func fetchJSON() {
guard let url = URL(string: "\(BaseURL.url)test.php"),
let value = variable.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "test=\(value)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode([Portfolio].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.person })
let keys = grouped.keys.sorted()
self.sections = keys.map({Section(name: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
Structure:
struct Section {
let name : String
var items : [Portfolio]
}
struct Portfolio: Decodable {
let person: String
let serial: String
var checkbox: Int
var isSelected : Bool {
get { return checkbox == 1 }
set { checkbox = newValue ? 1 : 0 }
}
enum CodingKeys : String, CodingKey {
case person, serial, checkbox
}
}
I think you can do that in multiple ways to your own custom sort. But for coding simplicity, you can consider this alternative solution. After sorting, remove, and insert the item again.
var keys = grouped.keys.sorted()
if let index = keys.firstIndex(of: "jack") {
let jack = keys.remove(at: index)
keys.insert(jack, at: 0)
}

How to populate loaded records from firebase?

I wrote the function to lad the records from firebase but there's an error
Escaping closure captures mutating 'self' parameter
The function is written as follows:
let db = Firestore.firestore()
#State var libraryImages: [LibraryImage] = []
mutating func loadImages() {
libraryImages = []
db.collection(K.FStore.CollectionImages.collectionName).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
for document in snapshotDocuments {
let documentData = document.data()
let title: String = documentData[K.FStore.CollectionImages.title] as! String
let thumbnailUrl: String = documentData[K.FStore.CollectionImages.thumbnailUrl] as! String
let svgUrl: String = documentData[K.FStore.CollectionImages.svgUrl] as! String
let libraryImageItem = LibraryImage(title: title, thumbnailUrl: thumbnailUrl, svgUrl: svgUrl)
self.libraryImages.append(libraryImageItem)
}
}
}
}
}
Does anyone know what is causing an error and how to get rid of it?
Move all this into reference type view model and use it as observed object in your view
Here is a demo of possible approach:
struct DemoView: View {
#ObservedObject var vm = ImagesViewModel()
// #StateObject var vm = ImagesViewModel() // << SwiftUI 2.0
var body: some View {
Text("Loaded images: \(vm.libraryImages.count)")
.onAppear {
self.vm.loadImages()
}
}
}
class ImagesViewModel: ObservableObject {
let db = Firestore.firestore()
#Published var libraryImages: [LibraryImage] = []
func loadImages() {
libraryImages = []
db.collection(K.FStore.CollectionImages.collectionName).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
var images = [LibraryImage]()
for document in snapshotDocuments {
let documentData = document.data()
let title: String = documentData[K.FStore.CollectionImages.title] as! String
let thumbnailUrl: String = documentData[K.FStore.CollectionImages.thumbnailUrl] as! String
let svgUrl: String = documentData[K.FStore.CollectionImages.svgUrl] as! String
let libraryImageItem = LibraryImage(title: title, thumbnailUrl: thumbnailUrl, svgUrl: svgUrl)
images.append(libraryImageItem)
}
DispatchQueue.main.async {
self.libraryImages = images
}
}
}
}
}
}

SwiftUI How can I pass in data into an ObservedObject function

I am new to SwiftUI and Swift . I got a Search Bar and a Listview whenever a user types something in the searchbar I do an http request and new data comes in . The issue is that the list is not updating with the new data and I think I know why . I need to pass my SearchBar response into the ObservedObject variable . I was reading this swiftui ObservedObject function call in all view however I still didn't find my answer . This is my code
struct RegistrationView: View {
#State private var searchTerm: String = ""
#State var txt = "" // Txt has the SearchBar text
#ObservedObject var getData = datas(location: "") // I need to pass it here
var body: some View {
VStack {
Text("Registration")
searchView(txt: $txt)
// datas(location: txt)
NavigationView {
List(getData.jsonData.filter{ txt == "" ? true : $0.name.localizedCaseInsensitiveContains(txt)}) { i in
ListRow(name: i.name,statelong: i.statelong)
}
}
.padding(.top, 5.0)
}
}
}
class datas: ObservableObject
{
#Published var jsonData = [datatype]()
init(location: String) {
let session = URLSession(configuration: .default)
if location == "" {
return
}
let parameter = "location=\(location)"
if location == "" {
return
}
let url = URL(string:"url")!
let request = RequestObject(AddToken: true, Url: url, Parameter: parameter)
session.dataTask(with:request, completionHandler: {(data, response, error) in
do
{
if data != nil
{
let fetch = try JSONDecoder().decode([datatype].self, from: data!)
DispatchQueue.main.async {
self.jsonData = fetch
print(fetch)
}
}
}
catch
{
print(error.localizedDescription)
}
}).resume()
}
}
In the above code I want to pass in the txt variable into the getData variable or do something like this #ObservedObject var getData = datas(location: txt) . When the SearchBar is updated then txt gets whatever is inserted into the SearchBar .
If I do something like this
#ObservedObject var getData = datas(location: "Me")
Then the list will update and correctly have everything that starts with Me my only issue is getting the SearchBar value inside datas so I don't have to hardcode things . As stated before I need to pass in txt to datas . Any help would be great
You don't need to init the class with that variable. You can just make a function for that and fetch it when ever you need. It could be just once.
class datas: ObservableObject {
#Published var jsonData = [datatype]()
func get(location: String) {
let session = URLSession(configuration: .default)
guard !location.isEmpty else { return }
let parameter = "location=\(location)"
let url = URL(string:"url")!
let request = RequestObject(AddToken: true, Url: url, Parameter: parameter)
session.dataTask(with:request, completionHandler: {(data, response, error) in
do {
guard data != nil else { return }
let fetch = try JSONDecoder().decode([datatype].self, from: data!)
DispatchQueue.main.async {
self.jsonData = fetch
print(fetch)
}
} catch {
print(error.localizedDescription)
}
}).resume()
}
}

RealmDB does not get populated

New to Realm... so hopefully a simple fix!
I have a data object of:
class GasFile : Object {
#objc dynamic var gasFilename : String = ""
// #objc dynamic var gasCategory : String? = ""
}
In my ViewController I have the following function:
func PopulateRealmWithFilenames() {
let fm = FileManager.default
var path = Bundle.main.resourcePath!
path += "/NBTFiles"
//let items = try! fm.contentsOfDirectory(atPath: path)
let items : [String] = try! fm.subpathsOfDirectory(atPath: path)
for item in items {
do {
print("item for realm is: \(item)")
try self.realm.write {
let newGasFile = GasFile()
newGasFile.gasFilename.append(item)
print("newGasFile written ok")
}
} catch {
print("Error writing new item to Realm \(error)")
}
}
}
This sets up the RealmDB ok, but never gets populated.
The aim of the function is to save all the filenames (not paths) of the files stored in a folder called NBTFiles.
I get the 'newGasfile written ok printed out' so it is getting into the loop.
datatype error ?
(I know I could just put it into an Array, but I want to use realm)
how do you want to add new object to realm without using adding function ?
you should use realm.add(object: newGasFile)
try this
func PopulateRealmWithFilenames() {
let fm = FileManager.default
var path = Bundle.main.resourcePath!
path += "/NBTFiles"
//let items = try! fm.contentsOfDirectory(atPath: path)
let items : [String] = try! fm.subpathsOfDirectory(atPath: path)
for item in items {
do {
print("item for realm is: \(item)")
try self.realm.write {
let newGasFile = GasFile()
newGasFile.gasFilename.append(item)
realm.add(object: newGasFile)
print("newGasFile written ok")
}
} catch {
print("Error writing new item to Realm \(error)")
}
}
}