I'm trying to pass a data object that contains a nested array of objects from the Master view to the Details view. Currently, I'm able to display the parent level of the data object, but I'm struggling to try to figure out how to pass it's a nested array of data on to the details page. Below is a sample of my data model and how I'm going about displaying the information currently. I'm not sure what I need to type for the code where I'm passing the information on in the NavigationLink element. I'm sure there's probably a better way to do this, however, I'm just a designer trying to figure this out for prototyping reasons.
//Passports.swift //
import Foundation
import SwiftUI
struct Passports: Identifiable {
let id : Int
let passportPremium: Bool
let passportActive: Bool
let passportTitle: String
let passportDates: String
let venues: [Venue]
}
struct Venue: Identifiable {
let id = UUID()
let title : String
let venueArea: String
let venueItems: [venueItem]
}
struct venueItem {
let title: String
let productDescription: String
let productPrice: Double
let productType: String
let newStatus: Bool
let diningPlan: Bool
let kidFriendly: Bool
let vegetarian: Bool
let glutenFree: Bool
let featuredProduct: Bool
let containsAlcohol: Bool
}
extension Passports {
static func all() -> [Passports] {
return [
Passports (
id: 1001,
passportPremium: false,
passportActive: true,
passportTitle : "Passport Title Example",
passportDates: "October 20 - November 3, 2019",
venues: [
Venue (
title: "Venue Name",
venueArea: "Germany",
venueItems: [
venueItem (
title: "Potato Dumpling",
productDescription: "Potato Dumpling with Mushroom Sauce",
productPrice: 0.00,
productType: "Food",
newStatus: false,
mealPlan: false,
kidApproved: true,
vegetarian: false,
glutenFree: false,
featuredProduct: false,
containsAlcohol: false
),
venueItem (
title: "Pork Schnitzel",
productDescription: "Pork Schnitzel with Mushroom Sauce and Spaetzle",
productPrice: 0.00,
productType: "Food",
newStatus: false,
mealPlan: false,
kidApproved: false,
vegetarian: false,
glutenFree: false,
featuredProduct: false,
containsAlcohol: false
)
])
]
)
]
}
}
//PassportsView //
import SwiftUI
struct PassportsView: View {
var model = Passports.all()
var body: some View {
NavigationView {
ForEach(self.model) { item in
NavigationLink (destination: PassportDetails(passportTitle: item.passportTitle, venues: [Venue()] ) ) {
GeometryReader { geometry in
HStack {
VStack(alignment: .leading) {
Text(item.passportTitle)
.fontWeight(.semibold)
.foregroundColor(Color.white)
.multilineTextAlignment(.leading)
Text(item.passportDates)
.foregroundColor(Color.white)
}.frame(width: geometry.size.width, height: 120)
.background(Color("Purple"))
.cornerRadius(6)
}
}
}.padding(.horizontal)
}
}
}
}
// PassportDetails //
struct PassportDetails: View {
var passportTitle = "Passport Name"
var venues: [Venue] = [
Venue(title: "Test Venue", venueArea: "Test Area", venueItems: [venueItem]())
]
var venueProd: [venueItem] = [
venueItem(title: "Test item", productDescription: "Test Description", productPrice: 9.99, productType: "Food", newStatus: false, mealPlan: true, kidApproved: false, vegetarian: false, glutenFree: false, featuredProduct: false, containsAlcohol: false)
]
var body: some View {
NavigationView {
List {
HStack {
Text("Test")
Spacer()
Text("9.99")
}
}
}.navigationBarTitle(Text(passportTitle))
}
}
In PassportDetails do the following:
var venues: [Venue]
and in Passports do this:
NavigationLink (destination: PassportDetails(passportTitle: item.passportTitle, venues: item.venues) )
PS: You should do the same for the passportTitle, meaning in PassportDetails put var passportTitle: String
Related
Allo, Hi, im new with SwiftUI and im facing some issue with the Yelp API and can't find answer anywhere... I'm creating a restaurant app and I want to add a on the business detail page a list of Yelp transactions that the business is registered for ("pickup", "delivery", and "restaurant_reservation").
I've try a lot of way to retrieved it but im about to give up... I don't know if it's me who's dumb or anything but my brain can't figured it out anymore. I've tried to get the data with "ForEach" and all any other way we usually get array data...
Second question (similar as the previous one) how can I retrieve the category alias/title from the API? I want to be able to filter the business based on their categories and also show on the business detail page the category associated to it.
Thank you :)
Yelp Response Body Example :
{
"total": 144,
"businesses": [
{
"id": "gR9DTbKCvezQlqvD7_FzPw",
"alias": "north-india-restaurant-san-francisco",
"price": "$$",
"url": "https://www.yelp.com/biz/north-india-restaurant-san-francisco",
"rating": 4,
"location": {
"zip_code": "94105",
"state": "CA",
"country": "US",
"city": "San Francisco",
"address2": "",
"address3": "",
"address1": "123 Second St"
},
"categories": [
{
"alias": "indpak",
"title": "Indian"
}
],
"phone": "+14153481234",
"coordinates": {
"longitude": -122.399305736113,
"latitude": 37.787789124691
},
"image_url": "http://s3-media4.fl.yelpcdn.com/bphoto/howYvOKNPXU9A5KUahEXLA/o.jpg",
"is_closed": false,
"name": "North India Restaurant",
"review_count": 615,
"transactions": ["pickup", "restaurant_reservation"]
},
// ...
]
}
Here is my Business model :
class Business: Decodable, Identifiable, ObservableObject {
#Published var imageData: Data?
var id: String?
var alias: String?
var name: String?
var imageUrl: String?
var isClosed: Bool?
var url: String?
var reviewCount: Int?
var categories: [Category]?
var rating: Double?
var coordinates: Coordinate?
var transactions: [String]?
var price: String?
var location: Location?
var phone: String?
var displayPhone: String?
var distance: Double?
enum CodingKeys: String, CodingKey {
case imageUrl = "image_url"
case isClosed = "is_closed"
case reviewCount = "review_count"
case displayPhone = "display_phone"
case id
case alias
case name
case url
case categories
case rating
case coordinates
case transactions
case price
case location
case phone
case distance
}
func getImageData() {
// Check that image url isn't nil
guard imageUrl != nil else {
return
}
// Download the data for the image
if let url = URL(string: imageUrl!) {
// Get a session
let session = URLSession.shared
let dataTask = session.dataTask(with: url) { (data, response, error) in
if error == nil {
DispatchQueue.main.async {
// Set the image data
self.imageData = data!
}
}
}
dataTask.resume()
}
}
static func getTestData() -> Business {
let b = Business()
return b
}
}
struct Category: Decodable {
var alias: String?
var title: String?
}
Here an example of my code :
struct BusinessDetail: View {
var business: Business
#State private var showDirections = false
var body: some View {
VStack (alignment: .leading) {
VStack (alignment:.leading, spacing:0) {
GeometryReader() { geometry in
// Business image
let uiImage = UIImage(data: business.imageData ?? Data())
Image(uiImage: uiImage ?? UIImage())
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
}
.ignoresSafeArea(.all, edges: .top)
// Open / closed indicator
ZStack (alignment: .leading) {
Rectangle()
.frame(height: 35)
.foregroundColor(business.isClosed! ? Color("icon-primary") : Color("background"))
Text(business.isClosed! ? "Closed" : "Open")
.foregroundColor(.white)
.font(.textHeader)
.padding(.leading)
}
}
Group {
HStack {
BusinessTitle(business: business)
.padding()
Spacer()
}
// Phone
HStack {
Text("Phone:")
.bold()
Text(business.displayPhone ?? "")
Spacer()
Link("Call", destination: URL(string: "tel:\(business.phone ?? "")")!)
}
.padding()
// Transactions
if business.transactions != nil {
ForEach(business.transactions!, id: \.self) { transaction in
Text(transaction)
.font(.bodyParagraph)
}
}
I'm using Swift to call Yelp's API to return local business information. In the JSON response, there are multiple nested arrays, i.e. "Category" that describes what type of business it is. I'm able to return the first item in the array, but I'm lost on how to use ForEach to return each individual item in the array.
The response looks like this:
{
"businesses": [
"id": XP5BuE79...,
"categories" : [
{
"alias": "golfcourse",
"title": "Golf Course"
},
{
"alias": "clubhouse",
"title": "Clubhouse"
}
],
....
}
My data model
struct BusinessesResponse: Codable {
enum CodingKeys: String, CodingKey {
case restaurants = "businesses"
}
let restaurants: [RestaurantResponse]
}
struct RestaurantResponse: Codable, Identifiable {
let id: String
var name: String
var image_url: String
var is_closed: Bool
var review_count: Int
var rating: Double
var distance: Double
var price: String?
var display_phone: String
var categories: [RestaurantCategory]
}
Currently, I'm able to return the first item in the "categories" array by using
VStack {
ForEach(fetcher.businesses) { restaurant in
VStack (alignment: .leading) {
WebImage(url: URL(string: restaurant.image_url))
.resizable()
.indicator(.activity) // Activity Indicator
.transition(.fade(duration: 0.5)) // Fade Transition with duration
.scaledToFill()
.frame(width: 400, height: 200, alignment: .center)
.clipped()
Text(restaurant.name)
.fontWeight(.bold)
HStack(spacing: 5.0) {
Text(restaurant.price ?? "No Pricing Info")
.font(.caption)
Text("·")
//Category displaying only first in nested array
Text(restaurant.categories[0].title)
.font(.caption)
Text("·")
Image(systemName: "star.fill")
.font(.caption)
Text("\(restaurant.rating, specifier: "%.1f")")
.font(.caption)
Text("(\(restaurant.review_count))")
.font(.caption)
Spacer()
}
}
}.padding(.bottom)
.padding(.horizontal, 20)
}
I am trying to assign the result that is retrieved by Alamofire to an array, and have come across an issue.
I am using the Stripe API for products, it returns back a JSON object called "data:", I am trying to assign that data object only to that products array I have.
Here's my code
var products: [Product] = []
let stripeProducts = "stripe-products-api"
func createArray() {
let stripeAuthHeader: HTTPHeaders = []
AF.request(stripeProducts, headers: stripeAuthHeader).responseJSON {
switch response.result {
case .failure(let error):
print(error)
case .success:
self.products = response.data \\ trying to access the data object from the JSON data
print(self.products)
}
}
}
JSON:
"object": "list",
"data": [
{
"id": "prod_123456",
"object": "product",
"active": true,
"attributes": [
],
"created": 1590423835,
"description": "Test",
"images": [
""
],
"livemode": false,
"metadata": {
"address": "Test"
},
"name": "Blue Jeans",
"statement_descriptor": null,
"type": "service",
"unit_label": null,
"updated": 1590653248
}
]
Thank you for your help.
You need to have Struct of Products
struct Datum: Decodable {
let id, object: String
let active: Bool
let created: Int
let datumDescription: String
let images: [String]
let livemode: Bool
let metadata: Metadata
let name: String
let type: String
let updated: Int
enum CodingKeys: String, CodingKey {
case id, object, active, created
case datumDescription = "description"
case images, livemode, metadata, name
case type
case updated
}
}
// MARK: - Metadata
struct Metadata: Decodable {
let address: String
}
Then parse it like this
let products = try? newJSONDecoder().decode(Products.self, from: response.data)
I'm trying to figure out the correct Syntax (or where I am going wrong?) for displaying returned multi nested json that has arrays containing further objects.
I can manage to get the nested objects to work but I get an error when trying to get the objects inside the array.
Here is my Struct:
import Foundation
struct spaceXLaunch: Codable {
var flight_number: Int
var mission_name: String?
var launch_date_local: String?
var rocket: rocketResponse
var launch_site: launchSiteResponse
var links: linksResponse
var launch_success: Bool?
var details: String?
}
extension spaceXLaunch: Identifiable {
var id: Int { return flight_number }
}
struct rocketResponse: Codable {
var rocket_name: String
var rocket_id: String
var first_stage: firstStageResponse
var second_stage: secondStageResponse
}
struct firstStageResponse: Codable {
var cores: [coresResponse]
}
struct coresResponse: Codable {
var landing_vehicle: String?
var land_success: Bool?
var reused: Bool?
var landing_type: String?
}
struct secondStageResponse: Codable {
var payloads: [payloadsResponse]
}
struct payloadsResponse: Codable {
var payload_id: String?
var payload_type: String?
var payload_mass_kg: Double?
}
struct launchSiteResponse: Codable {
var site_name: String
}
struct linksResponse: Codable {
var missionPatch: String?
var missionPatchSml: String?
private enum CodingKeys: String, CodingKey {
case missionPatch = "mission_patch"
case missionPatchSml = "mission_patch_small"
}
}
Here is the SwiftUI code Im using to render the view
import SwiftUI
struct listView: View {
#ObservedObject var fetcher = LaunchDataFetcher()
var body: some View {
List(fetcher.launches) { spaceXLaunch in
VStack (alignment: .leading) {
Text(spaceXLaunch.mission_name ?? "No Data Found")
Spacer()
Text(spaceXLaunch.details ?? "No Data Found")
.font(.system(size: 11))
Spacer()
Text(spaceXLaunch.launch_date_local ?? "No Data Found")
Spacer()
Text(spaceXLaunch.rocket.rocket_name)
Spacer()
///This line below comes back with an error of "Cannot subscript a value of incorrect or ambiguous type"
Text(spaceXLaunch.rocket.first_stage.cores[0].landing_type)
Spacer()
}
}
}
}
struct listView_Previews: PreviewProvider {
static var previews: some View {
listView()
.environment(\.colorScheme, .dark)
}
}
Here is a sample of the Json data returned for the api
{
"flight_number": 24,
"mission_name": "CRS-7",
"mission_id": [
"EE86F74"
],
"launch_year": "2015",
"launch_date_unix": 1435501260,
"launch_date_utc": "2015-06-28T14:21:00.000Z",
"launch_date_local": "2015-06-28T10:21:00-04:00",
"is_tentative": false,
"tentative_max_precision": "hour",
"tbd": false,
"launch_window": 0,
"rocket": {
"rocket_id": "falcon9",
"rocket_name": "Falcon 9",
"rocket_type": "v1.1",
"first_stage": {
"cores": [
{
"core_serial": "B1018",
"flight": 1,
"block": 1,
"gridfins": true,
"legs": true,
"reused": false,
"land_success": null,
"landing_intent": true,
"landing_type": "ASDS",
"landing_vehicle": "OCISLY"
}
]
},
"second_stage": {
"block": 1,
"payloads": [
{
"payload_id": "CRS-7",
"norad_id": [],
"reused": false,
"cap_serial": "C109",
"customers": [
"NASA (CRS)"
],
"nationality": "United States",
"manufacturer": "SpaceX",
"payload_type": "Dragon 1.1",
"payload_mass_kg": 2477,
"payload_mass_lbs": 5460.9,
"orbit": "ISS",
"orbit_params": {
"reference_system": "geocentric",
"regime": "low-earth",
"longitude": null,
"semi_major_axis_km": null,
"eccentricity": null,
"periapsis_km": null,
"apoapsis_km": null,
"inclination_deg": 51.6,
"period_min": null,
"lifespan_years": null,
"epoch": null,
"mean_motion": null,
"raan": null,
"arg_of_pericenter": null,
"mean_anomaly": null
},
"mass_returned_kg": null,
"mass_returned_lbs": null,
"flight_time_sec": 139,
"cargo_manifest": "https://www.nasa.gov/sites/default/files/atoms/files/spacex_crs-7_mission_overview.pdf",
"uid": "UerI6qmZTU2Fx2efDFm3QQ=="
}
]
},
"fairings": null
},
"ships": [
"ELSBETH3",
"GOQUEST",
"GOSEARCHER"
],
"telemetry": {
"flight_club": null
},
"launch_site": {
"site_id": "ccafs_slc_40",
"site_name": "CCAFS SLC 40",
"site_name_long": "Cape Canaveral Air Force Station Space Launch Complex 40"
},
"launch_success": false,
"launch_failure_details": {
"time": 139,
"altitude": 40,
"reason": "helium tank overpressure lead to the second stage LOX tank explosion"
},
"links": {
"mission_patch": "https://images2.imgbox.com/47/39/stH98Qy1_o.png",
"mission_patch_small": "https://images2.imgbox.com/d0/22/gyTVYo21_o.png",
"reddit_campaign": null,
"reddit_launch": "https://www.reddit.com/r/spacex/comments/3b27hk",
"reddit_recovery": null,
"reddit_media": "https://www.reddit.com/r/spacex/comments/3berj3",
"presskit": "https://www.nasa.gov/sites/default/files/atoms/files/spacex_nasa_crs-7_presskit.pdf",
"article_link": "https://spaceflightnow.com/2015/06/28/falcon-9-rocket-destroyed-in-launch-mishap/",
"wikipedia": "https://en.wikipedia.org/wiki/SpaceX_CRS-7",
"video_link": "https://www.youtube.com/watch?v=PuNymhcTtSQ",
"youtube_id": "PuNymhcTtSQ",
"flickr_images": [
"https://farm1.staticflickr.com/344/19045370790_f20f29cd8d_o.jpg",
"https://farm1.staticflickr.com/287/18999110808_6e153fed64_o.jpg"
]
},
"details": "Launch performance was nominal until an overpressure incident in the second-stage LOX tank, leading to vehicle breakup at T+150 seconds. The Dragon capsule survived the explosion but was lost upon splashdown because its software did not contain provisions for parachute deployment on launch vehicle failure.",
"upcoming": false,
"static_fire_date_utc": "2015-06-26T05:00:00.000Z",
"static_fire_date_unix": 1435294800,
"timeline": {
"webcast_liftoff": 61,
"go_for_prop_loading": -2280,
"rp1_loading": -2100,
"stage1_lox_loading": -2100,
"stage2_lox_loading": -960,
"engine_chill": -420,
"prelaunch_checks": -60,
"propellant_pressurization": -60,
"go_for_launch": -45,
"ignition": -3,
"liftoff": 0,
"maxq": 60,
"meco": 180,
"stage_sep": 180,
"second_stage_ignition": 180,
"seco-1": 540,
"dragon_separation": 600,
"dragon_solar_deploy": 720,
"dragon_bay_door_deploy": 8400
},
"crew": null
},
Thanks to Chris for pointing out that adding an optional (??) at the end of this line
Text(spaceXLaunch.rocket.first_stage.cores[0].landing_type)
so it looks like this:
Text(spaceXLaunch.rocket.first_stage.cores[0].landing_type ?? "No Data Found")
and it all works.
Generate your JSONModel structs with following tool https://app.quicktype.io . Then validated your logic.
i am trying to push array in document array my collection is
{
"_id": "58eed81af6f8e3788de703f9",
"first_name": "abc",
"vehicles": {
"exhibit": "18",
"title": "Motor Velicle Information for Donald French",
"details": [
{
"year": "",
"make_model": "",
"registered_owner": "",
"license_number": "",
"date_of_purchase": "",
"purchase_price": ""
}
]
}
}
so what i want is to push data in details for that i had try like this
Licensee.update({"_id":"58eed81af6f8e3788de703f9"},{
$push:{
"vehicles.details":data
}
},function(err,data){
if(!err)
{
console.log('data',data);
}
else
{
console.log('err',err);
}
});
and for this i create one schema i don't know is right or not
var licSchema = new SimpleSchema({
"_id":{
type:String,
label:"_id",
optional: false,
},
"vehicles.details.year": {
type: String,
label: "year",
optional: true,
},
"vehicles.details.make_model": {
type: String,
label: "make_model",
optional: true,
}
});
where is my fault please give me solution .
Error Uncaught Error: After filtering out keys not in the schema, your modifier is now empty
You can try this. AddToSet should be the right way.
const schema = new SimpleSchema({
"vehicles.details.$.year": {
type: String,
label: "year",
optional: true,
},
"vehicles.details.$.make_model": {
type: String,
label: "make_model",
optional: true,
}
});
Licensee.update({"_id":"58eed81af6f8e3788de703f9"},{
$addToSet:{
"vehicles.details": data
}
});