Im trying to pass data into a swiftui view to be displayed when it is initialized, but am having trouble getting it to work.
Here is my code which passes data into my 'MarkerInfoWindow.swift' view:
func mapView(_ mapView: GMSMapView, markerInfoContents marker: GMSMarker) -> UIView? {
print("Showing marker infowindow")
print("marker.userData: \(marker.userData)")
let mUserData = marker.userData as? [String:String]
print("mUserData?['name']", (mUserData?["name"]) ?? "mUserData[name] was nil")
let mInfoWindow = UIHostingController(rootView: MarkerInfoWindow(placedata: mUserData!))
return mInfoWindow.view
}
Here is the code to my 'MarkerInfoWindow.swift' view:
struct PlaceDataStruct {
var name : String
var place_id : String
}
struct MarkerInfoWindow: View {
var placedata: [PlaceDataStruct]
var body: some View {
//Here is where i keep getting errors regardless if i use this method or dot notation
Text(placedata["name"])
}
}
Im not sure if im implementing my PlaceDataStruct incorrectly.
Does anyone know what I'm doing wrong, so that I can display the right data every time my view is initialized?
You’re MarkerInfoWindow is expecting an array of structs but the data you are passing is a dictionary, in the form [String: String]
You should update your MarkerInfoWindow to accept a dictionary instead. Here is an example of how to do that.
struct MarkerInfoWindow: View {
var placedata: [String: String] // update to be dictionary
var body: some View {
Text(placedata["name", default: ""]) // use correct notation for accessing dictionary
}
}
You’re also force unwrapping the data before you pass it to your MarkerInfoWindow if that’s your intention that is ok. But note that if your data isn’t there then it will crash your app.
Related
So I have an api request that requests a bunch of data from a fake api url, the data I am getting is being put on a placeholder, I just want to have a global variable to be able to use that array of codable data in my collectionviews.
struct productsList{
static var itemsList = [ProductItem]()
}
func getProducts() {
storeRepo
.getAllProducts()
.subscribe { result in
productsList.itemsList = result
for item in productsList.itemsList{
print(item.category)
}
} onError: { error in
print(error.localizedDescription)
}.disposed(by: disposeBag)
}
func printReuslt() {
for i in productsList.itemsList{
print(i.id)
}
}
note that it's not printing the printResult() but it's looping inside of the .subscribe
note that i am using Moya as well as RXswift
What you're looking for is called a Singleton. Swift makes this extremely easy to do. Basically, the short and sweet is that the struct you create, initializes itself as a property of itself. Anytime you access (In this example) APIHandler.shared you'll get a reference to the only single object, which has your other properties dataObj1 and someObj2 from this example.
class APIHandler {
let shared = APIHandler()
var dataObj1: YourObj?
var someObj2: YourObj2?
init() {
self.someObj1 = yourMethodCall()
self.someObj2 = someCalculation()
}
}
This is how you access it from another class. BE CAREFUL you can access APIHandler.someObj which would result in a null reference exception if you don't have an object created, so when doing this always access the shared property.
class MainClass {
let apiHandler: APIHandler?
override func viewDidLoad(...) {
super.viewDidLoad(...)
apiHandler = APIHandler.shared
}
}
I'm not sure why this code doesn't work. It builds and runs fine, but nothing is displayed in the view. While debugging, I can see that the append() calls don't actually add the items to the array, but there's no error:
import SwiftUI
struct Test: View {
#State private var array: [String] = []
var body: some View {
self.array.append("A")
self.array.append("B")
self.array.append("C")
return VStack {
ForEach(self.array, id: \.self) {string in
Text(string)
}
}
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
I've also tried doing the appends in an init(_:) with the same result. It works if I initialize the array with the values like #State private var array: [String] = ["A", "B", "C"] so it seems like there's some immutability of state variables nuance that I'm missing. I'm pretty new to Swift. Can someone please explain what I'm doing wrong here?
I tried to run your code on Xcode 11.4.1, I got a warning saying;
Modifying state during view update, this will cause undefined behavior
This error occurs because you’re trying to modify the state of a SwiftUI view while it is actually being rendered.
So as an alternative you can try appending items in onAppear block
struct Test: View {
State private var array: [String] = []
var body: some View {
VStack {
ForEach(self.array, id: \.self) {string in
Text(string)
}
}.onAppear { // Prefer, Life cycle method
self.array.append("A")
self.array.append("B")
self.array.append("C")
}
}
}
Then you'll be able to see your items in the screen.
Also here is a bit detailed explanation
I'm trying to retrieve data from Parse using findObjectsInbackground and store it in an array. I've already created outside the scoop of viewDidLoad().
I managed retrieving and printing the data, but they are not stored in the array and it keeps being empty!
I used self.articalsTableView.reloadData() but unfortunately the array myArray is still empty.
Any help please, this has been confusing me for two days!
import UIKit
import Parse
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var articalsTableView: UITableView!
var myArray = [PFObject]()
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Gateshead")
query.findObjectsInBackground {(objects, error) -> Void in
if error != nil {
print(error)
} else {
if let articals = objects {
for artical in articals {
// print (articles) THIS IS WOKING WELL
self.myArray.append(artical)
self.articalsTableView.reloadData()
}
}
}
}
}
}
findObjectsInback is using a background thread and you should dispatch to mainThread whenever if you want to access UIKit stuff in this case updating self.articalsTableView.
Also self.articalsTableView.reloadData() should be called at the end (not in the loop). This to prevent a race condition on self.myArray being accessed by the main-thread (to update self.articalsTableView ) and by the background-thread (to append artical)
for artical in articals {
// print (articles) THIS IS WOKING WELL
self.myArray.append(artical)
}
DispatchQueue.main.async {
self.articalsTableView.reloadData()
}
I'm trying to create an array of two items: a URL and a String. The index of the items is of significance.
The data is taken from QuartzFilterManager, which provides information about the Quartz Filters installed on a system.
import Cocoa
import Quartz
class myFilter {
var myURL: URL?
var name: String = ""
}
func getFilters() -> Array<String> {
var filterArray: Array<myFilter>
if let Filters = QuartzFilterManager.filters(inDomains: nil) {
for (index, eachFilter) in Filters.enumerated() {
filterArray[index].name.append((eachFilter as! QuartzFilter).localizedName()!)
filterArray[index].myURL!.append((eachFilter as! QuartzFilter).url()!)
}
}
}
Xcode complains about Type URL not having an append method. But the name property in the preceding line works. In short, how do I set specific properties in my array?
There are lots of issues.
Make myFilter a struct instead of class and name it properly as MyFilter.
You never initialize your filterArray, you only declare it.
You need to create a new instance of MyFilter for each url/name pair you want to add to the array.
You don't need to use enumerated in this case.
You have the wrong return type for your getFilter function (I think).
Use proper naming conventions.
Here's cleaned up code:
struct MyFilter {
var myURL: URL?
var name: String
}
func getFilters() -> [MyFilter] {
var filterArray = [MyFilter]()
if let filters = QuartzFilterManager.filters(inDomains: nil) {
for eachFilter in filters {
let filter = MyFilter(myURL: (eachFilter as! QuartzFilter).url(), name: (eachFilter as! QuartzFilter).localizedName()!)
filterArray.append(filter)
}
}
return filterArray
}
It's still not ideal. Having to cast eachFilter using as! QuartzFilter is clunky.
And other uses of ! are bad. Force-unwrapping the call to localizedName() can crash. Consider proper solutions.
Append is a method of the array struct, not of the URL/String.
You first need to create the array (you just declared it, you actually need to assign something to it)
You then need to create the object that you want to append into the array
You can now append this newly created object to the array
It should look something like this:
import Cocoa
import Quartz
class MyFilter {
var myURL: URL?
var name: String?
init(url: URL?, name: String?) {
self.myURL = url
self.name = name
}
}
func getFilters() -> Array<MyFilter> {
var filterArray = [MyFilter]()
if let filters = QuartzFilterManager.filters(inDomains: nil) {
for filter in filters {
let aFilter = MyFilter(url: filter.url(), name: filter.localizedName())
filterArray.append(aFilter)
}
}
return filterArray
}
Now the array returned by this method will have N MyFilter objects.
You can access every object in the array the way you did before, with
let aFilter = filterArray[index]
And to get the property inside of that object:
let url = aFilter.myURL
let name = aFilter.name
PS: I changed some names to fit the swift conventions (classes are written in PascalCase and variables in camelCase)
PpS: be careful with ! in swift, if it's used on something that happens to be nil will crash the app. Read more about optionals here
PpPs: I was just a few minutes late :D
I'm trying to fetch relational data from firebase in swift 3 and storing it in an array. It does fetch everything the way I want it to but i can't access the final array to work with.
I have tried everything I found online but can't make it work properly.
There are 3 child nodes I'm fetching, so every time it fetches it appends it to the array.
The output is:
success
success
success
I just want it to print "success" once.
Here is my code:
// Here the child with the relations is loaded
func fetchFollowingSection1IDs() {
guard let userID = FIRAuth.auth()?.currentUser?.uid else { return }
let reference = FIRDatabase.database().reference().child("interests").child("relations").child("userAndSection1").child(userID)
reference.observe(.childAdded, with: { (snapshot) in
// It's supposed to fetch the details of Section1 according to the childs from the relations (they are the IDs of Section1)
self.fetchSection1(section1ID: snapshot.key, completionHandler: { success in
guard success == true else {
return
}
print("success")
self.collectionView?.reloadData()
})
}, withCancel: nil)
}
// Here it gets the details from Firebase
func fetchSection1(section1ID: String, completionHandler: #escaping (Bool) -> ()) {
let ref = FIRDatabase.database().reference().child("interests").child("details").child("country").child("section1").child(section1ID)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
self.collectionView?.refreshControl?.endRefreshing()
if let dictionary = snapshot.value as? [String: AnyObject] {
let section1 = Section1New(section1ID: section1ID, dictionary: dictionary)
self.section1s.append(section1)
}
completionHandler(true)
}) { (err) in
print("Failed to fetch section1s:", err)
}
}
My Firebase structure for the relations looks like this:
"interests" : {
"relations" : {
"userAndSection1" : {
"7fQvYMAO4yeVbb5gq1kEPTdR3XI3" : { // this is the user ID
"-KjS8r7Pbf6V2f0D1V9r" : true, // these are the IDs for Section1
"-KjS8tQdJbnZ7cXsNPm3" : true,
"-KjS8unhAoqOcfJB2IXh" : true
},
}
Everything loads properly and populates my collection views. It is just the problem that it is the wrong number of Section1s because of the triple appending to the array.
Thank you for your answers!
The code is doing exactly what you are telling it to do.
Your firebase event is .childAdded so it will iterate over each child node one at a time.
It first loads -KjS8r7Pbf6V2f0D1V9r and adds it to the section1s array - there is then one item in the array.
Then it loads -KjS8tQdJbnZ7cXsNPm3 and appends to the array. There's two items in the array and two lines output. etc.
The only thing we don't see in the code in your question is the line that actually prints the array, which is probably in your collectionView delegate methods.
Depending on your use case, you may want to read everything in with .value, and then iterate over that to populate your dataSource array.