SWIFT UI add element to array - xcode

I trying to learn the new SwiftUI coding technique. I would like to click a button that will add elements to an array that is a #State variable. The button is the buttonclick function. The array is the push_group_row / push_group_array. I get an error in the append statement.
Eventually the buttonclick will access a database to build an array with more row, but for now I just trying to add one row.
Code:
import SwiftUI
import Combine
var gordon: String = "xxxxxx"
struct Result: Codable {
let trackId: Int
let trackName: String
let collectionName: String
}
struct Response: Codable {
var results: [Result]
}
struct Pokemon: Identifiable {
let id: Int
let name: String
let type: String
let color: Color
}
struct push_group_row {
let id: Int
let code: String
let title: String
}
struct ContentView: View
{
#State private var results = [Result]()
#State var pokemonList = [
Pokemon(id: 0, name: "Charmander", type: "Fire", color: .red),
Pokemon(id: 1, name: "Squirtle", type: "Water", color: .blue),
Pokemon(id: 2, name: "Bulbasaur", type: "Grass", color: .green),
Pokemon(id: 3, name: "Pikachu", type: "Electric", color: .yellow),]
#State var push_group_array = [push_group_row(id: 0, code: "code12", title: "POAFire")]
var body: some View
{
NavigationView
{
VStack(alignment: . leading){
Button(action: {
// What to perform
self.buttonclick()
}) {
// How the button looks like
Text("clickme")
.background(Color.purple)
.foregroundColor(.white)
}
List(results, id: \.trackId)
{item in
NavigationLink(destination: DetailView(lm: String(item.trackId)))
{
VStack(alignment: .leading)
{
Text(String(item.trackId))
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
Text(gordon)
}
}
}
List(self.pokemonList)
{ pokemon in
HStack
{
Text(pokemon.name)
Text(pokemon.type).foregroundColor(pokemon.color)
}
}
List(push_group_array, id: \.id)
{ pg_item in
HStack
{
Text(String(pg_item.id))
Text(pg_item.code)
}
}
.onAppear(perform: self.loaddata)
}
.navigationBarTitle("x")
.navigationBarItems(
trailing: Button(action: addPokemon, label: { Text("Add") }))
Spacer()
}
}
func addPokemon() {
if let randomPokemon = pokemonList.randomElement() {
pokemonList.append(randomPokemon)
}
}
// *************************** below is the add arrat code
func buttonclick() {
let newCode = "First"
let newTitle = "Second"
push_group_array.append(id: 1, code: newCode, title: newTitle)
}
func loaddata()
{
print("loaddata")
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song")
else
{
print("Invalid URL")
return
}
var urlData: NSData?
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)
{
DispatchQueue.main.async
{
urlData = data as NSData?
self.results = decodedResponse.results
print(self.results)
print(urlData ?? "urlData_Defaultvalue")
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

You need to push the object rather than 3 values
push_group_array.append(push_group_row(id: 1, code: newCode, title: newTitle))

Related

Unable to place Bool value in a toggle in swiftui

Hey all I have been trying to fix this issue for 2 days now and just can't seem to get what I am lookinhg for.
My working code:
struct User: Decodable {
let name: String
let description: String
let isOn: Bool
}
struct ContentView: View {
#State var users = [User]()
var body: some View {
List{
ForEach(users, id: \.name) { item in
HStack {
Toggle(isOn: .constant(true)) {
Label {
Text(item.description)
} icon: {
//list.img
}
}
}.padding(7)
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://-----.---/jsontest.json") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try?
decoder.decode([User].self, from: data) {
DispatchQueue.main.async {
self.users = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
the Json its pulling looks like this:
{
"name": "J",
"description": "Echo Show",
"isOn": true
},...
Like I said above this code works as-is. Where the problem I am having comes into play is the toggle part. It doesn't seem to be happens with the item.isOn for seeing if its true or false. The only thing it likes is the .constant(). Naturally this will not work for my case due to me needing to change the toggles to either true or false.
If it's anything other than .constant it has the error of:
Cannot convert value of type 'Bool' to expected argument type 'Binding'
I can perhaps if I do this
#State var ison = false
var body: some View {
List{
ForEach(users, id: \.name) { item in
HStack {
Toggle(isOn: $ison)) {
That seems to take that error away.... but how can it get the value thats looping for isOn that way?
What am I missing here?
try this approach, making isOn a var in User, and using bindings, the $ in the ForEach and Toggle, works for me:
struct User: Decodable {
let name: String
let description: String
var isOn: Bool // <-- here
}
struct ContentView: View {
#State var users = [User]()
var body: some View {
List{
ForEach($users, id: \.name) { $item in // <-- here
HStack {
Toggle(isOn: $item.isOn) { // <-- here
Label {
Text(item.description)
} icon: {
//list.img
}
}
}.padding(7)
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://-----.---/jsontest.json") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try?
decoder.decode([User].self, from: data) {
DispatchQueue.main.async {
self.users = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}

Integer Picker to select in swiftui xcode

I am making a project with qr code generator and i don't know how to use integer with picker and i want to ask what code did i missed in the function and the view. does any expert know how to solve it, thank you for the help.
my code:
#State private var sSecond = Int()
#State var navigated = false
let Second = ["10", "20", "30", "40", "50", "60"]
var body: some View {
Form{
Section {
VStack{
Picker(selection: $sSecond, label: Text("Select Seconds"))
{
ForEach(0 ..< Second.count) {
index in Text(self.Second[index]).tag(index)
}
}
}
}
NavigationLink(destination: Generate(Second: $sSecond), isActive: self.$navigated)
{
Text("Complete")
}
}
Function:
import Foundation
import SwiftUI
import CoreImage.CIFilterBuiltins
struct Generate: View {
#State var second = Int()
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var body: some View {
Image(uiImage: imageGenerate(second))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}
func imageGenerate(second: Int)-> UIImage {
let data = Data(second)
filter.setValue(data, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
How Can I Show 10 in qr code, thats what i expected in this questions
As for your first question: Picker on Int goes like this:
struct PickerInt: View {
let secondsArray = [10, 20, 30, 40, 50, 60] // Int instead of String
#State private var sSecond: Int = 10
var body: some View {
Form{
Picker(selection: $sSecond, label: Text("Select Seconds"))
{
ForEach(secondsArray, id: \.self) { sec in
Text("\(sec)").tag(sec)
}
}
}
}
}

Struggling with rapid api and swiftui

Hi I am struggling with getting football data api from rapidapi to work in swift ui . here is the code below
The errors is get are "self.team = decodedTeams" Cannot find 'self' in scope"
and in my content view i get for " $network.getTeams"
Value of type 'EnvironmentObject.Wrapper' has no dynamic member 'getTeams' using key path from root type 'Network'
I have set out what i have in 2 pages of my swiftui code below
any help would be appreciated, I am really struggling with this one
// Network.swift
// Football Scores
//
//
import Foundation
class Network: ObservableObject {
#Published var teams: [Team] = []
}
func getTeams() {
let headers = [
"X-RapidAPI-Key": "MY API KEY",
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com"
]`
let request = NSMutableURLRequest(url: NSURL(string: "https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=39")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
DispatchQueue.main.async {
do {
let decodedTeams = try JSONDecoder().decode([Team].self, from: data)
self.team = decodedTeams
} catch let error {
print("Error decoding: ", error)
}
}
}
})
dataTask.resume()
}
and
//
// Team.swift
// Football Scores
//
//
import Foundation
struct Team: Identifiable, Decodable {
var id: Int
var name: String
var logo: String
var points: String
var goaldif: String
}
and
// Football_ScoresApp.swift
// Football Scores
//
import SwiftUI
import Foundation
#main
struct Football_ScoresApp: App {
var network = Network()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(network)
}
}
}
and
import SwiftUI
import CoreData
struct ContentView: View {
#EnvironmentObject var network: Network
var body: some View {
ScrollView {
Text("All teams")
.font(.title).bold()
}
.onAppear {
network.getTeams()
}
VStack(alignment: .leading) {
ForEach(network.teams) { team in
HStack(alignment:.top) {
Text("\(team.id)")
VStack(alignment: .leading) {
Text(team.name)
.bold()
}
}
.frame(width: 300, alignment: .leading)
.padding()
.background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301)))
.cornerRadius(20)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(Network())
}
}
To display the teams from your api request, first you need to create a set of struct models that
represent the json data that the server is sending. From the server response, you need to
extract the teams information you want to display. Here is my code
that shows how to do it, works well for me, pay attention to the details:
struct ContentView: View {
#StateObject var network = Network() // <-- for testing
var body: some View {
List {
VStack(alignment: .leading) {
ForEach(network.teams) { team in
HStack(alignment:.top) {
Text("\(team.id)")
Text(team.name).bold()
}
.frame(width: 300, alignment: .leading)
.padding()
.background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301)))
.cornerRadius(20)
}
}
}
.onAppear {
network.getTeams()
}
}
}
class Network: ObservableObject {
#Published var teams: [Team] = []
func getTeams() {
let token = "your-key" // <--- here your api key
guard let url = URL(string: "https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=39") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("\(token)", forHTTPHeaderField: "X-RapidAPI-Key")
request.setValue("api-football-v1.p.rapidapi.com", forHTTPHeaderField: "X-RapidAPI-Host")
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
do {
let results = try JSONDecoder().decode(FootyResponse.self, from: data)
// extract just the teams
for response in results.response {
for stand in response.league.standings {
for league in stand {
self.teams.append(league.team)
}
}
}
} catch {
print(error) // <-- here important
}
}
}.resume()
}
}
// MARK: - FootyResponse
struct FootyResponse: Codable {
let welcomeGet: String
let parameters: Parameters
let errors: [String]
let results: Int
let paging: Paging
let response: [Response]
enum CodingKeys: String, CodingKey {
case welcomeGet = "get"
case parameters, errors, results, paging, response
}
}
// MARK: - Paging
struct Paging: Codable {
let current, total: Int
}
// MARK: - Parameters
struct Parameters: Codable {
let league, season: String
}
// MARK: - Response
struct Response: Codable {
let league: League
}
// MARK: - League
struct League: Codable {
let id: Int
let name: String
let country: String
let logo: String
let flag: String
let season: Int
let standings: [[Standing]]
}
// MARK: - Standing
struct Standing: Codable {
let rank: Int
let team: Team
let points, goalsDiff: Int
let group: String
let form: String
let status: String
let standingDescription: String?
let all, home, away: All
let update: String // <-- Date
enum CodingKeys: String, CodingKey {
case rank, team, points, goalsDiff, group, form, status
case standingDescription = "description"
case all, home, away, update
}
}
// MARK: - All
struct All: Codable {
let played, win, draw, lose: Int
let goals: Goals
}
// MARK: - Goals
struct Goals: Codable {
let goalsFor, against: Int
enum CodingKeys: String, CodingKey {
case goalsFor = "for"
case against
}
}
// MARK: - Team
struct Team: Identifiable, Codable {
let id: Int
let name: String
let logo: String
}
EDIT-1:
to get the points of each team, you need to use the Standing struct. Here is an example code to do that.
struct ContentView: View {
#StateObject var network = Network()
var body: some View {
List {
VStack(alignment: .leading) {
ForEach(network.stand) { stand in // <-- here
HStack(alignment:.top) {
Text(stand.team.name).bold() // <-- here
Text("\(stand.points) points") // <-- here
}
.frame(width: 300, alignment: .leading)
.padding()
.background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301)))
.cornerRadius(20)
}
}
}
.onAppear {
network.getTeams()
}
}
}
class Network: ObservableObject {
#Published var teams: [Team] = []
#Published var stand: [Standing] = [] // <-- here
func getTeams() {
let token = "your-key" // <--- here your api key
guard let url = URL(string: "https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=39") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("\(token)", forHTTPHeaderField: "X-RapidAPI-Key")
request.setValue("api-football-v1.p.rapidapi.com", forHTTPHeaderField: "X-RapidAPI-Host")
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
do {
let results = try JSONDecoder().decode(FootyResponse.self, from: data)
// extract the teams and the standings
results.response.forEach{ response in
response.league.standings.forEach{ stand in
self.stand = stand // <--- here
stand.forEach{ league in
self.teams.append(league.team)
}
}
}
} catch {
print(error) // <-- here important
}
}
}.resume()
}
}
// MARK: - Standing
struct Standing: Identifiable, Codable {
let id = UUID() // <-- here
let rank: Int
let team: Team
let points, goalsDiff: Int
let group: String
let form: String
let status: String
let standingDescription: String?
let all, home, away: All
let update: String // <-- Date
enum CodingKeys: String, CodingKey {
case rank, team, points, goalsDiff, group, form, status
case standingDescription = "description"
case all, home, away, update
}
}

Deleting an item from a list based on the element UUID

I feel a bit embarrassed for asking this, but after more than a day trying I'm stuck. I've had a few changes on the code based on replies to other issues. The latest code essentially selects the items on a list based on the UUID.
This has caused my delete function to stop working since I was working with passing an Int as the selected element to be deleted. I was originally implementing things like this.
Code follows, I'm still trying to figure out my way around SwiftUI, but question is, how can I now delete items on a list (and on the array behind it) based on a UUID as opposed to the usual selected item.
In case it makes a difference, this is for macOS Big Sur.
Code:
struct NoteItem: Codable, Hashable, Identifiable {
let id: Int
var text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
var tags: [String] = []
}
struct AllNotes: View {
#EnvironmentObject private var data: DataModel
#State var noteText: String = ""
#State var selectedNoteId: UUID?
var body: some View {
NavigationView {
List(data.notes) { note in
NavigationLink(
destination: NoteView(note: note),
tag: note.id,
selection: $selectedNoteId
) {
VStack(alignment: .leading) {
Text(note.text.components(separatedBy: NSCharacterSet.newlines).first!)
Text(note.dateText).font(.body).fontWeight(.light)
}
.padding(.vertical, 8)
}
}
.listStyle(InsetListStyle())
}
.navigationTitle("A title")
.toolbar {
ToolbarItem(placement: .navigation) {
Button(action: {
data.notes.append(NoteItem(id: UUID(), text: "New Note", date: Date(), tags: []))
}) {
Image(systemName: "square.and.pencil")
}
}
ToolbarItem(placement: .automatic) {
Button(action: {
// Delete here????
}) {
Image(systemName: "trash")
}
}
}
.onAppear {
DispatchQueue.main.async {
selectedNoteId = data.notes.first?.id
}
}
.onChange(of: data.notes) { notes in
if selectedNoteId == nil || !notes.contains(where: { $0.id == selectedNoteId }) {
selectedNoteId = data.notes.first?.id
}
}
}
}
The original removeNote I had is the following:
func removeNote() {
if let selection = self.selectedItem,
let selectionIndex = data.notes.firstIndex(of: selection) {
print("delete item: \(selectionIndex)")
data.notes.remove(at: selectionIndex)
}
}
could you try this:
struct NoteItem: Codable, Hashable, Identifiable {
let id: UUID // <--- here
var text: String
var date = Date()
var dateText: String = ""
var tags: [String] = []
}
func removeNote() {
if let selection = selectedNoteId,
let selectionIndex = data.notes.firstIndex(where: { $0.id == selection }) {
print("delete item: \(selectionIndex)")
data.notes.remove(at: selectionIndex)
}
}

How to make navigation link to expandable list in SwiftUI

I am trying to make navigation links to an expandable list.
I want to make navigation links only to sub-lists like "UICollectionView", "UIScrollView", "NavigationView", and "Expanding Rows".
But I don't know how to deal with this problem.
If someone helped me, I would appreciate it.
import SwiftUI
struct TutorialItem: Identifiable {
let id = UUID()
let title: String
var tutorialItems: [TutorialItem]?
}
struct ContentView: View {
var body: some View {
let tutorialItems: [TutorialItem] = [sampleUIKit(), sampleSwiftUI()]
List(tutorialItems, children: \.tutorialItems){
tutorial in
Text(tutorial.title)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
func sampleUIKit() -> TutorialItem {
return .init(title: "UIKit", tutorialItems:
[.init(title: "UICollectionView"),
.init(title: "UIScrollView")])
}
func sampleSwiftUI() -> TutorialItem {
return .init(title: "SwiftUI", tutorialItems:
[.init(title: "NavigationView"),
.init(title: "Expanding Rows")])
}
'''
I tried and below worked for me
#State var favItems = [BookmarkItem]()
var body: some View {
if #available(iOS 14.0, *) {
List {
ForEach(favItems) { item in
Section(header: Text(item.title)) {
OutlineGroup(
item.bookmarkItems ?? [],
id: \.id,
children: \.bookmarkItems
) { tree in
NavigationLink(destination: Text("-- \(tree.desc)")) {
Text("\(tree.desc)")
.font(.subheadline)
}
}
}
}
}.listStyle(SidebarListStyle())
} else {
// Fallback on earlier versions
}
}
//BookmarkItems
struct BookmarkItem: Identifiable {
var id = UUID()
var title: String
var desc: String
var bookmarkItems: [BookmarkItem]?
}

Resources