SwiftUI NavigationLink not sending data to another view [closed] - ios

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
This is my contentView.swift file. I am trying to send data from one view to another view using navigationLink but I can't. I am learning the swiftUI and iOS development. So I am confused what I did wrong.
import SwiftUI
import URLImage
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var trackId: Int
var trackName: String
var collectionName: String
var artworkUrl100: URL
}
struct ContentView: View {
#State var results = [Result]()
var body: some View {
NavigationView {
List(results, id: \.trackId) { item in
NavigationLink(
destination: SingleView(item: item),
label: {
HStack() {
URLImage(item.artworkUrl100) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, height: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.cornerRadius(5.0)
}
VStack(alignment: .leading) {
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
.font(.subheadline)
.foregroundColor(.secondary)
Spacer()
}
}
})
}
.onAppear(perform: loadData)
.navigationTitle("Native App")
}
}
func loadData() {
guard let url = URL(string: "https://itunes.apple.com/search?term=arijit&entity=song") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
// we have good data – go back to the main thread
DispatchQueue.main.async {
// update our UI
self.results = decodedResponse.results
}
// everything is good, so we can exit
return
}
}
// if we're still here it means there was a problem
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I want to send value to SingleView.swift page . but no luck.
My SingleView.swift code is:
import SwiftUI
struct SingleView: View {
let item: Result
var body: some View {
Text("")
}
}
struct SingleView_Previews: PreviewProvider {
static var previews: some View {
SingleView(item: Result)
}
}
Please let me know where I did wrong. Thank you.

Working fine with some changes to SingleView
import SwiftUI
struct SingleView: View {
let item: Result
var body: some View {
Text(item.trackName)
}
}
struct SingleView_Previews: PreviewProvider {
static var previews: some View {
SingleView(item: Result(trackId: 1, trackName: "Track Name", collectionName: "Coll name", artworkUrl100: URL(string: "https://stackoverflow.com/questions/68556148/swiftui-navigationlink-not-sending-data-to-another-view")!))
}
}

Related

SwiftUI how to lazy load a stack and async changing their value

I want to use lazyStack to load my data and use DispatchQueue to update its value after a specific time.
But the view doesn't change and I don't know how to refresh the value in the view
import SwiftUI
struct CustomImages{
var image:Image
var id = 0
init(){
print("loading")
self.image = Image("UnknownAlbum")
self.id = 1
}
}
struct SwiftUIView: View {
var body: some View {
VStack{
ScrollView {
LazyVStack {
ForEach(0..<100){row in
var i = CustomImages()
HStack{
i.image
Text("\(i.id)")
.onAppear{
DispatchQueue.main.asyncAfter(deadline: .now()){
print("adding")
i.id += 2
}
}
}
}
}
}
}
}
}
Variables in Custom Images should be linked through #Binding.
In SwiftUI, a typical declaration cannot detect variation.
I've used the example code, and I think you can change it according to your purpose.
In the example code, the logic changes to the second image after 3 seconds.
import SwiftUI
struct ContentView: View {
#State private var image = Image("farnsworth")
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<30) { row in
let id = Binding<Int>(get: { row }, set: {_ in})
let customImages = CustomImages(image: $image, id: id)
HStack {
customImages.image
.resizable()
.aspectRatio(contentMode: .fit)
Text("\(customImages.id)")
}
.padding()
.onAppear {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
image = Image("farnsworth2")
}
}
}
}
}
}
}
struct CustomImages{
#Binding var image: Image
#Binding var id: Int
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

How to setup NavigationLink inside SwiftUI list

I am attempting to set up a SwiftUI weather app. when the user searches for a city name in the textfield then taps the search button, a NavigationLink list item should appear in the list. Then, the user should be able to click the navigation link and re-direct to a detail view. My goal is to have the searched navigation links to populate a list. However, my search cities are not populating in the list, and I'm not sure why. In ContentView, I setup a list with a ForEach function that passes in cityNameList, which is an instance of the WeatherViewModel. My expectation is that Text(city.title) should display as a NavigationLink list item. How should I configure the ContentView or ViewModel to populate the the list with NavigationLink list items? See My code below:
ContentView
import SwiftUI
struct ContentView: View {
// Whenever something in the viewmodel changes, the content view will know to update the UI related elements
#StateObject var viewModel = WeatherViewModel()
#State private var cityName = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter City Name", text: $cityName).textFieldStyle(.roundedBorder)
Button(action: {
viewModel.fetchWeather(for: cityName)
cityName = ""
}, label: {
Text("Search")
.padding(10)
.background(Color.green)
.foregroundColor(Color.white)
.cornerRadius(10)
})
List {
ForEach(viewModel.cityWeather, id: \.id) { city in
NavigationLink(destination: DetailView(detail: viewModel)) {
HStack {
Text(city.cityWeather.name)
.font(.system(size: 32))
}
}
}
}
Spacer()
}
.navigationTitle("Weather MVVM")
}.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ViewModel
import Foundation
class WeatherViewModel: ObservableObject {
//everytime these properties are updated, any view holding onto an instance of this viewModel will go ahead and updated the respective UI
#Published var cityWeather: WeatherModel = WeatherModel()
func fetchWeather(for cityName: String) {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=imperial&appid=<MyAPIKey>") else {
return
}
let task = URLSession.shared.dataTask(with: url) { data, _, error in
// get data
guard let data = data, error == nil else {
return
}
//convert data to model
do {
let model = try JSONDecoder().decode(WeatherModel.self, from: data)
DispatchQueue.main.async {
self.cityWeather = model
}
}
catch {
print(error)
}
}
task.resume()
}
}
Model
import Foundation
struct WeatherModel: Identifiable, Codable {
var id = UUID()
var name: String = ""
var main: CurrentWeather = CurrentWeather()
var weather: [WeatherInfo] = []
func firstWeatherInfo() -> String {
return weather.count > 0 ? weather[0].description : ""
}
}
struct CurrentWeather: Codable {
var temp: Float = 0.0
}
struct WeatherInfo: Codable {
var description: String = ""
}
DetailView
import SwiftUI
struct DetailView: View {
var detail: WeatherViewModel
var body: some View {
VStack(spacing: 20) {
Text(detail.cityWeather.name)
.font(.system(size: 32))
Text("\(detail.cityWeather.main.temp)")
.font(.system(size: 44))
Text(detail.cityWeather.firstWeatherInfo())
.font(.system(size: 24))
}
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView(detail: WeatherViewModel.init())
}
}
try something like this example code, works well for me:
struct WeatherModel: Identifiable, Codable {
let id = UUID()
var name: String = ""
var main: CurrentWeather = CurrentWeather()
var weather: [WeatherInfo] = []
func firstWeatherInfo() -> String {
return weather.count > 0 ? weather[0].description : ""
}
}
struct CurrentWeather: Codable {
var temp: Float = 0.0
}
struct WeatherInfo: Codable {
var description: String = ""
}
struct ContentView: View {
// Whenever something in the viewmodel changes, the content view will know to update the UI related elements
#StateObject var viewModel = WeatherViewModel()
#State private var cityName = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter City Name", text: $cityName).textFieldStyle(.roundedBorder)
Button(action: {
viewModel.fetchWeather(for: cityName)
cityName = ""
}, label: {
Text("Search")
.padding(10)
.background(Color.green)
.foregroundColor(Color.white)
.cornerRadius(10)
})
List {
ForEach(viewModel.cityNameList) { city in
NavigationLink(destination: DetailView(detail: city)) {
HStack {
Text(city.name).font(.system(size: 32))
}
}
}
}
Spacer()
}.navigationTitle("Weather MVVM")
}.navigationViewStyle(.stack)
}
}
struct DetailView: View {
var detail: WeatherModel
var body: some View {
VStack(spacing: 20) {
Text(detail.name).font(.system(size: 32))
Text("\(detail.main.temp)").font(.system(size: 44))
Text(detail.firstWeatherInfo()).font(.system(size: 24))
}
}
}
class WeatherViewModel: ObservableObject {
#Published var cityNameList = [WeatherModel]()
func fetchWeather(for cityName: String) {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=imperial&appid=YOURKEY") else { return }
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else { return }
do {
let model = try JSONDecoder().decode(WeatherModel.self, from: data)
DispatchQueue.main.async {
self.cityNameList.append(model)
}
}
catch {
print(error) // <-- you HAVE TO deal with errors here
}
}
task.resume()
}
}

SwiftUI iOS 14 View does not Update #Published Array with #EnvironmentObject

I'm working on a calorie-tracker app.
In my App, I can open the Detail side of some products, set the amount and add the product to the "Cart".
Later, I want to read out all collected datas from the array and show them an a short overview.
But this View won't be updated after making changer on the array.
Due to I storage the datas with the userDefaults, I always have to reopen the app to update the view. Only then, the hole array will be displayed.
My Class Cart:
import Foundation
struct Order: Equatable, Identifiable, Codable {
var id = UUID()
var product: Product
var modifier: Double
var date: Date
}
class Cart: ObservableObject {
#Published var orders = [Order]()
static let saveKey = "SavedData"
init() {
if let data = UserDefaults.standard.data(forKey: Self.saveKey) {
if let decoded = try? JSONDecoder().decode([Order].self, from: data) {
self.orders = decoded
}
} else {
self.orders = []
}
}
// save order
func save() {
if let encoded = try? JSONEncoder().encode(self.orders) {
UserDefaults.standard.set(encoded, forKey: Self.saveKey)
}
}
// add to order
func add(order: Order) {
self.orders.append(order)
print("product added to cart")
save()
}
// remove from order
func remove(order: Order) {
if let index = orders.firstIndex(of: order) {
orders.remove(at: index)
}
}
}
I made a View to apply the amount of any special product.
import SwiftUI
struct AmountView: View {
#EnvironmentObject var cart: Cart
#State private var textInput = ""
#State private var orderFinished = false
var product: Product
func StringDoubleConverter(text: String) -> String {
return String(format: "%.2f", Double(textInput.replacingOccurrences(of: ",", with: ".")) ?? 0)
}
var body: some View {
VStack {
Form {
Section(header: Text("Mengenangabe")) {
// input for the amount
AmountInputView(textInput: $textInput)
if !orderFinished {
Button("Hinzufügen", action: {
orderFinished = true
hideKeyboard()
// add product to the cart
self.cart.add(order: Order(product: product, modifier: Double(StringDoubleConverter(text: textInput))!, date: Date()))
})
.disabled(textInput == "")
.animation(.default)
} else {
Text("Wurde zum Logbuch hinzugefügt")
.foregroundColor(.blue)
}
}
productNutritionCollectionView(product: product, modifier: Double(StringDoubleConverter(text: textInput))!)
}
}
}
}
struct AmountView_Previews: PreviewProvider {
static var previews: some View {
AmountView(product: Product.exampleProduct).environmentObject(Cart())
}
}
Then, I want to display all products in the order in a logbook view using a Form and a ForEach lope.
struct LogbookView: View {
func deleteProducts(at offsets: IndexSet) {
cart.orders.remove(atOffsets: offsets)
cart.save()
}
#EnvironmentObject var cart: Cart
#State private var date = Date()
var body: some View {
NavigationView {
Form {
Section(header: Text("List")) {
ForEach(cart.orders) { order in
Text(order.product.name)
}
.onDelete(perform: { indexSet in
deleteProducts(at: indexSet)
})
}
}
.navigationBarTitle(Text("Logbuch"), displayMode: .automatic)
.navigationBarItems(trailing: DateView(date: $date))
}
}
}
struct LogbookView_Previews: PreviewProvider {
static var previews: some View {
LogbookView().environmentObject(Cart())
}
}
I'm using a AppTab View to navigate the app. Therefore, I changed the AppTab View in the main Struct to the default View with an environment object of Cart.
#main
struct KalorientrackerApp: App {
var body: some Scene {
WindowGroup {
AppTabView().environmentObject(Cart())
}
}
}
struct KalorientrackerApp_Previews: PreviewProvider {
static var previews: some View {
Text("Hello, World!")
}
}
I'm opening my AmountView using a .sheet
struct ProductDetailView: View {
#State private var showAmountView = false
let product: Product
var body: some View {
VStack {
// placeholder Image
Image(product.fullImage)
.clipShape(Circle())
.padding(.top, 5)
Spacer()
Form {
productNutritionCollectionView(product: product, modifier: 100)
}
}
// Titel for Navigation bar
.navigationBarTitle(Text(product.name), displayMode: .inline)
// Button to go to amount view
.navigationBarItems(trailing: Button(action: {
self.showAmountView = true
}, label: {
Image(systemName: "plus.circle")
.padding(.leading, 20)
}).sheet(isPresented: $showAmountView, content: {
AmountView(product: product).environmentObject(Cart())
}))
}
}
struct ProductDetailView_Previews: PreviewProvider {
static var previews: some View {
ProductDetailView(product: Product.exampleProduct) }
}
I already found some other discussions, but they didn't worked for me.
I'm using Xcode 12 beta 6 and iOS14 beta 6
I found the bug myself. The problem was, that I committed explicit an .environmentObject in my .sheet action.
AmountView(product: product).environmentObject(Cart())
I removed .environmentObject(Cart()) from the .sheet action. Now it's working.
Thinking this caused the bug because I'm using the .environmentObject(Cart()) operator in the main View of my project.

IOS swiftUI can't read json from local files [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm new with IOS development, I'm using swiftUI and following this tutorial https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation but I have been stuck here for a while, In the tutorial, they pass one item from a json file in the project
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarkData[0])
}
}
I'm trying to do the same with my own data, I have a file called sorteosData.json but when I try to pass the first item to the preview it says "use of unresolved identifier"
struct PastSorteoRowView_Previews: PreviewProvider {
static var previews: some View {
PastSorteoRowView(sorteo: sorteosData[0])
}
Xcode doesn't recognize sorteosData[0], how can I solve this?
I have a followed all the steps from the tutorial, but for some reason, I can't pass my data to the preview
With JSON you need use JSONDecoder(). Assume that we have this file:
[
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
},
{
"name": "Orange",
"points": 100
}
]
For convenience you can create a struct (convenient even for nested elements):
struct product: Codable, Hashable {
var name: String
var points: Int
var description: String?
}
Create a function to parse your JSON in bundle called list.json that return an array of struct product:
func jsonTwo() -> [product]{
let url = Bundle.main.url(forResource: "list", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
let products = try? decoder.decode([product].self, from: data)
return products!
}
Finally set your interface:
var body: some View {
List{
ForEach(jsonTwo(), id: \.self) { item in
VStack(alignment: .leading, spacing: 0){
Text("name:\(item.name) - points:\(item.points)")
Text("\(item.description ?? "")")
}
}
}
}
Complete code:
struct product: Codable, Hashable {
var name: String
var points: Int
var description: String?
}
struct ContentView: View {
func jsonTwo() -> [product]{
let url = Bundle.main.url(forResource: "list", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
let products = try? decoder.decode([product].self, from: data)
return products!
}
var body: some View {
List{
ForEach(jsonTwo(), id: \.self) { item in
VStack(alignment: .leading, spacing: 0){
Text("name:\(item.name) - points:\(item.points)")
Text("\(item.description ?? "")")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

SwiftUI Bug Fix "Cannot convert value of type 'Binding<int>'to expected type 'Binding<_>?'"

I'm having trouble with this error :
"Cannot convert value of type 'Binding'to expected type 'Binding<_>?'"
I think I'm coding in an out dated version of SwiftUI but I'm not 100% sure, so any help I can get will be awesome.
I displayed my code down below so you can take a look at it.
I'm not sure if the bottom part matters but I added it just to be safe.
ContentView
import SwiftUI
struct ContentView: View {
#State private var selection = 0
#State var networkManager = NetworkManager()
var body: some View {
TabView(selection: $selection){
NavigationView{
Text("First View")
.font(.title)
.navigationBarTitle(Text("Welcome"))
}
.tabItem {
VStack {
Image(systemName: "star.fill")
Text("Welcome")
}
}
.tag(0)
NavigationView{
List(networkManager.featureList.results.identified(by: \.url)) { featured in
Text(featured.name.capitalized)
}
.navigationBarTitle(Text("Featured"))
}
.tabItem {
VStack {
Image(systemName: "app.badge.fill")
Text("Featured")
}
}
.tag(1)
NavigationView{
Text("First View")
.font(.title)
.navigationBarTitle(Text("Repos"))
}
.tabItem {
VStack {
Image(systemName: "rectangle.stack.fill")
Text("Repos")
}
}
.tag(2)
NavigationView{
Text("First View")
.font(.title)
.navigationBarTitle(Text("Request"))
}
.tabItem {
VStack {
Image(systemName: "icloud.and.arrow.down.fill")
Text("Request")
}
}
.tag(3)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ApiView
import Foundation
import SwiftUI
import Combine
class NetworkManager: ObservableObject {
var didChange = PassthroughSubject<NetworkManager, Never>()
var featureList = FeaturedApiList(results: []){
didSet{
didChange.send(self)
}
}
init(){
guard let url = URL(string: "https://myurl.com/repos.json") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
let featureList = try! JSONDecoder().decode(FeaturedApiList.self, from: data)
DispatchQueue.main.async {
self.featureList = featureList
}
}.resume()
}
}
Thanks!
The error shown is very confusing, but some other errors would cause this sort of error.
In your case, you may need to fix the second NavigationView in the TabView:
NavigationView{
//↓Fix this line.
List(networkManager.featureList.results, id: \.url) { featured in
Text(featured.name.capitalized)
}
.navigationBarTitle(Text("Featured"))
}
.tabItem {
VStack {
Image(systemName: "app.badge.fill")
Text("Featured")
}
}
.tag(1)
Better check this thread and always try to find an up-to-date samples or tutorials.
Welcome to Stackoverflow!
OOPer is actually correct. You will need to fix your List.
Let's try to replace your data for the List just to see that the project will compile:
Suppose we have a model that conforms to Identifiable protocol, like so:
struct Person: Identifiable {
var id = UUID()
var name: String
}
And then replace your List line with this:
List([Person(name: "fafa")]) { featured in
Text(featured.name)
}
This time it should run. Since SwiftUI is new, this must be the reason why the error is quite confusing.

Resources