Mark Core Data entries as Favourites SWIFTUI - json

I am working on an app that stores posts from on-line Json to CoreData for offline content. It is a Real Estate Listings app where users sell and rent properties.
So far i accomplish to save fethed listings in CoreData but I can not implement "Add to Favourites" for the existing entries. I use NSPredicate to filter the listings types .in different views
I have created a entity "Listing" Core Data - Listing
And a "Favorite" entity Core Data - Favorites
My question is: How can i achieve "Add to Favourite"
Option A: Save the "is favourite" listing one more time in another Entity.
Option B: Create in the "Listing" Entity another "is favourite" property and keep it in Core Data as long as is favourite?
// Here is my Listing Model.swift
struct ListingModel: Hashable, Decodable, Encodable, Identifiable {
var id : Int
var title : String
var category : String
var name : String
var image : String
var publishdate : String
var saleprice : Int = 0
var rentprice : Int = 0
var listingtype : String
var latitude : Double!
var longitude : Double!
}
// JSONViewModel.swift
import SwiftUI
import CoreData
class JSONViewModel: ObservableObject {
#Published var listings: [ListingModel] = []
// #Published var tags :[Tags] = []
// saving Json to Core Data...
func saveData(contex: NSManagedObjectContext) {
listings.forEach { (data) in
let entity = Listing(context: contex)
entity.title = data.title
entity.name = data.name
entity.category = data.category
entity.image = data.image
entity.publishdate = data.publishdate
entity.tagline = data.tagline
entity.content = data.content
entity.coverimage = data.coverimage
entity.saleprice = Int64(truncating: NSNumber(value: data.saleprice))
entity.rentprice = Int64(truncating: NSNumber(value: data.rentprice))
entity.phone = data.phone
entity.email = data.email
entity.area = Int64(truncating: NSNumber(value: data.area))
entity.rooms = Int64(truncating: NSNumber(value: data.rooms))
entity.beds = Int64(truncating: NSNumber(value: data.beds))
entity.bathrooms = Int64(truncating: NSNumber(value: data.bathrooms))
entity.expires = data.expires
entity.adresa = data.adresa
entity.listingtype = data.listingtype
entity.latitude = Double(truncating: NSNumber(value: data.latitude))
entity.longitude = Double(truncating: NSNumber(value: data.longitude))
}
// }
// saving all pending data at once
do{
try contex.save()
print("success")
}
catch{
print(error.localizedDescription)
}
}
func fetchData(context: NSManagedObjectContext){
let url = "https://... my api adress"
var request = URLRequest(url: URL(string: url)!)
request.addValue("swiftui2.0", forHTTPHeaderField: "field")
let session = URLSession(configuration: .default)
session.dataTask(with: request) { (data, res, _) in
guard let jsonData = data else{return}
// check for errors
let response = res as! HTTPURLResponse
// checking by status code
if response.statusCode == 404 {
print("error Api Errror")
}
// fetching JSON Data ..
do {
let listings = try JSONDecoder().decode([ListingModel].self, from: jsonData)
DispatchQueue.main.async {
self.listings = listings
self.saveData(contex: context)
}
}
catch {
print(error.localizedDescription)
}
}
.resume()
}
// try to extend the function
func addListingToFavourites(favouritelisting:ListingModel) {
addRecordToFavourites(favouritelisting:favouritelisting.title)
}
func isListingFavourite(favouritelisting:ListingModel) -> Bool {
if let _ = fetchRecord(favouritelisting:favouritelisting.title) {
return true
}
return false
}
func removeListingFromFavourites(favouritelisting:ListingModel) {
removeRecordFromFavourites(favouritelisting:favouritelisting.title)
}
func toggleFavourite(favouritelisting:ListingModel) {
if isListingFavourite(favouritelisting: favouritelisting) {
removeListingFromFavourites(favouritelisting: favouritelisting)
}
else {
addListingToFavourites(favouritelisting: favouritelisting)
}
}
}
and i created also extension JSONViewModel:
extension JSONViewModel {
private func addRecordToFavourites(favouritelisting:String) {
guard let context = managedContext else {return}
if let record = fetchRecord(favouritelisting:favouritelisting) {
print("record \(record) already exists")
return
}
let entity = NSEntityDescription.entity(forEntityName: "Favourite",
in: context)!
let favourite = NSManagedObject(entity:entity, insertInto:context)
favourite.setValue(favouritelisting, forKeyPath:"favouritelisting")
do {
try context.save()
}
catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
self.changed = true
}
private func fetchRecord(favouritelisting:String) -> Favourite? {
guard let context = managedContext else {return nil}
let request = NSFetchRequest<Favourite>(entityName: "Favourite")
request.predicate = NSPredicate(format: "favouritelisting == %#", favouritelisting)
if let users = try? context.fetch(request) {
if users.count > 0 {
return users[0]
}
}
return nil
}
private func removeRecordFromFavourites(favouritelisting:String) {
guard let context = managedContext else {return}
if let record = fetchRecord(favouritelisting:favouritelisting) {
context.delete(record)
self.changed = true
}
}
}
::: Am i on the wright way? still i don't know it this what i should do!
Please find bellow Latest listingsView
import SwiftUI
struct latestListings: View {
#StateObject var jsonModel = JSONViewModel()
#Environment(\.managedObjectContext) var context
#FetchRequest(entity: Listing.entity(),
sortDescriptors:
[NSSortDescriptor(keyPath: \Listing.publishdate, ascending: false)])
var results : FetchedResults<Listing>
var textHeight: CGFloat = 60
var fullWidth: CGFloat = UIScreen.main.bounds.width
var cardWidthHalf: CGFloat = UIScreen.main.bounds.width / 2 + UIScreen.main.bounds.width / 3
var spacing: CGFloat = 10
var viewHeight: CGFloat = UIScreen.main.bounds.height / 2
#State private var isError = false
var body: some View {
VStack(alignment: .leading, spacing: 20) {
VStack(alignment: .leading, spacing: 10) {
HStack {
HStack {
Text("Latest Listings")
.modifier(textSectionTitle())
Spacer()
NavigationLink (destination: AllListingsVertical()) {
ViewMoreButton()
}.buttonStyle(PlainButtonStyle())
}
.padding(.trailing, 20)
}
Divider()
.modifier(dividerStyle())
HStack {
Image(systemName: "wand.and.stars.inverse")
.modifier(textSectionIcon())
Text("Manualy download listings to device storage. Useful for offline use.")
.modifier(textSectionTagline())
}
}
.padding(.top, 10)
.padding(.leading, 20)
.padding(.bottom, 0)
ScrollView(.horizontal, showsIndicators: false, content: {
HStack {
// checkin if core data exists
if results.isEmpty{
if jsonModel.listings.isEmpty{
HStack(alignment: .center) {
HStack(spacing: 10) {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: Color("dinamicPillsGrass")))
.scaleEffect(2, anchor: .center)
// fetching data
.onAppear(perform: {
jsonModel.fetchData(context: context)
})
}.frame(width: UIScreen.main.bounds.width)
}.modifier(cardHeight())
// when array is clear indicator appears
// as result data is fetched again
}
else{
HStack(spacing: 20) {
ForEach(jsonModel.listings,id: \.self){listing in
NavigationLink (destination: CardDetailView(listing: listing)) {
HStack {
CardViewOrizontal(listing: listing)
}
}.buttonStyle(PlainButtonStyle())
// display fetched Json Data..
}
}
}
}
else{
// results.prefix(?) unde ? cata articole sa arate
HStack(spacing: 20) {
ForEach(results.prefix(10)){listing in
NavigationLink (destination: CardDetailView(fetchedData: listing)) {
HStack {
CardViewOrizontal(fetchedData: listing)
}
}.buttonStyle(PlainButtonStyle())
}
}
.padding(.trailing, 15)
.padding(.leading, 15)
}
// update finish
}.padding(.top, 10)
.padding(.bottom, 10)
})
VStack(alignment: .center) {
Button(action: {
// clearing data in core data..
if Reachability.isConnectedToNetwork() {
//
do{
jsonModel.listings.removeAll()
results.forEach { (listing) in context.delete(listing) }
try context.save()
}
catch{
print(error.localizedDescription)
}
print("Network is connected")
self.isError = false
} else {
print("Network is not connected")
self.isError = true
}
}, label: {
HStack(alignment: .center) {
Image(systemName: "icloud.and.arrow.down")
.modifier(textSectionIcon())
Text("Update")
.modifier(textSectionTagline())
}
.padding(5)
.padding(.trailing, 5)
.background(Color("blueLeading"))
.cornerRadius(20)
.modifier(shadowPills())
}).alert(isPresented: $isError) {
Alert(title: Text("Network is not connected"),
message: Text("WiFi or Cellular not availible. You can still browse offline content!"),
dismissButton: .default(Text("OK")))
}
}.frame(width: fullWidth)
}
.padding(.top, 10)
.padding(.bottom, 60)
.frame(width: fullWidth)
.background(LinearGradient(gradient: Gradient(colors: [Color("dinamicGray1"), Color("dinamicGray2")]), startPoint: .top, endPoint: .bottom))
.cornerRadius(20)
.padding(.top, -50)
.modifier(shadowSection())
}
}
And CardDetailView
//
// CardDetailView.swift
// WebyCoreData
//
// Created by Marius Geageac on 20.12.2020.
//
import SwiftUI
import KingfisherSwiftUI
import MapKit
struct CardDetailView: View {
// noul liked
var fullWidth: CGFloat = UIScreen.main.bounds.width
var halfScreenH: CGFloat = UIScreen.main.bounds.height / 2
#ObservedObject var settingsVM = SettingsViewModel()
#State private var isVisible = false
var listing: ListingModel?
var fetchedData: Listing?
// Modifiers
var cardWidth: CGFloat = UIScreen.main.bounds.width
var imageWidth: CGFloat = UIScreen.main.bounds.width
/// map
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: listing == nil ? fetchedData!.latitude : listing!.latitude,
longitude: listing == nil ? fetchedData!.longitude : listing!.longitude)
}
let paddingPills: CGFloat = 5
let textSizePills: CGFloat = 14
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 5) {
HStack {
Text(listing == nil ? fetchedData!.title! : listing!.title)
.modifier(textSectionTitle())
}
Divider()
.modifier(dividerStyle())
HStack {
Image(systemName: "info.circle")
.modifier(textSectionIcon())
Text(listing == nil ? fetchedData!.tagline! : listing!.tagline)
.modifier(textSectionTagline())
}
}
.padding(.top, 10)
.padding(.leading, 20)
.padding(.bottom, 0)
if self.fetchedData!.isFavorite == false {
Button(action: {
fetchedData!.isFavorite.toggle()
}) {
Image(systemName: "heart.circle")
.modifier(textSectionIcon())
}.padding()
}
else {
Button(action: {
fetchedData!.isFavorite.toggle()
}) {
Image(systemName: "heart.circle.fill")
.modifier(textSectionIcon())
}.padding()
}
}
}
}
import SwiftUI
struct Favorites: View {
#StateObject var jsonModel = JSONViewModel()
var cardWidth: CGFloat = UIScreen.main.bounds.width - 30
var fullWidth: CGFloat = UIScreen.main.bounds.width
// #StateObject var jsonModel = JSONViewModel()
#Environment(.managedObjectContext) var context
// Fetching Data From Core Data..
#FetchRequest(entity: Listing.entity(), sortDescriptors:
// [NSSortDescriptor(keyPath: \Listing.publishdate, ascending: false)])
[NSSortDescriptor(keyPath: \Listing.publishdate, ascending: false),],predicate: NSPredicate(format: "isFavourite == %#" , NSNumber(value: true)))
var results : FetchedResults<Listing>
var body: some View {
ScrollView(.vertical) {
VStack(alignment: .center) {
VStack(alignment: .center) {
LazyVStack(spacing: 20) {
ForEach(results){listing in
NavigationLink (destination: CardDetailView(fetchedData: listing)) {
VStack {
CardView(fetchedData: listing)
}.frame(width: UIScreen.main.bounds.width)
.modifier(cardHeight())
}.buttonStyle(PlainButtonStyle())
}
}
}
}.padding(.top, 20)
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
allListingsTitlePill() // Title
}
ToolbarItem(placement: .navigationBarTrailing){
HStack {
Button(action: {
}, label: {
Image(systemName: "heart.circle.fill")
.font(.system(size: 40, weight: .regular))
})
}
}
}
}
}

I would personally just add another property in your listings entity called isFavourite and set it to a boolean. set it initially to false.
Then when you are doing your fetch requests, you can only show favourites using a predicate like this.
let predicateIsFavourite = NSPredicate(format: "isFavourite == %#", NSNumber(value: true))
and in lists / ForEach, you display whether it is a favourite and use a button to toggle it being a favourite.
The toggle would just set the value of isFavourite to true (let me know if you would like some code for that but looking at your question, it seems like you know how to do that)

Related

How to change the url string with inputs from the user in SwiftUI

i am making this app that uses jikan API to show a list of animes, so in the url there is an option to change stuff like the type - anime, manga, etc and the subtype - upcoming, tv, movie, etc, the API is working working fine and is fetching details but now I want to show two pickers in a form view preferably to allow the user to select type and subtype thus I have used #State properties for the picker but it's not updating the list when I run the app and select a different value from the picker
here is the code -
import SwiftUI
struct Response: Codable{
var top: [Result]
}
struct Result: Codable {
var mal_id: Int
var rank: Int
var title: String
var type: String
var start_date: String?
var image_url: String
}
struct ContentView: View {
#State private var str2 = ""
#State private var str3 = ""
func loadData() {
str3 = String("/\(subtype[subTypeSelection])")
str2 = String("\(type[typeSelection])/")
let str1 = "https://api.jikan.moe/v3/top/"
guard let url = URL(string: str1 + str2 + "\(1)" + str3) 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.top = decodedResponse.top
}
// 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()
}
var type = ["anime", "manga", "people", "characters"]
var subtype = ["airing", "upcoming", "tv", "movie", "ova", "special"]
#State private var page = 1
#State private var typeSelection = 0
#State private var subTypeSelection = 2
#State private var top = [Result]()
var body: some View {
// ScrollView {
VStack {
Picker("Your Selection", selection: $subTypeSelection) {
ForEach(0 ..< 6) {
somehting in
Text("\(subtype[somehting])")
}
}
Picker("Your Selection", selection: $typeSelection) {
ForEach(0 ..< 4) {
somehting in
Text("\(type[somehting])")
}
}
}
// .onAppear(perform: {
// loadData()
// })
List(top, id: \.mal_id) { item in
HStack {
AsyncImage(url: URL(string: item.image_url)!,
placeholder: { Text("Loading ...") },
image: { Image(uiImage: $0).resizable() })
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
// .frame(idealHeight: UIScreen.main.bounds.width / 10 * 10 )
.clipShape(Capsule())
VStack(alignment: .leading) {
Text(item.title)
.font(.headline)
Text(String("\(item.rank)"))
.font(.headline)
Text(item.type)
.font(.headline)
Text(item.start_date ?? "")
.font(.headline)
}
}
}
// }
.onAppear(perform: loadData)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
you could try to use "onChange" on each picker:
VStack {
Picker("Your subtype Selection", selection: $subTypeSelection) {
ForEach(0 ..< 6) { somehting in
Text("\(subtype[somehting])").tag(somehting)
}
}.onChange(of: subTypeSelection) { value in
loadData()
}
Picker("Your type Selection", selection: $typeSelection) {
ForEach(0 ..< 4) { somehting in
Text("\(type[somehting])").tag(somehting)
}
}.onChange(of: typeSelection) { value in
loadData()
}
}
It's not very efficient, but I'm sure you will find a better way to re-load the data.

Core Data - Favorited posts become duplicate when fetching JSON again

In my app i fetch Json entries and save it directly in CoreData.
I added aditional Boolean IsFavorite (default False) to the entyti to let user mark as favorite and display it in a separate view.
So far so good, the problem is that when i fetch again to refres the content from updated json the Posts witch are alreadi favorite get doplicated.
This is how i fetch and save the data:
class JSONViewModel: ObservableObject {
#Published var listings: [ListingModel] = []
// #Published var tags :[Tags] = []
// saving Json to Core Data...
func saveData(contex: NSManagedObjectContext) {
listings.forEach { (data) in
let entity = Listing(context: contex)
// entity.id = Int64(data.id)
entity.title = data.title
entity.name = data.name
entity.expires = data.expires
entity.adresa = data.adresa
entity.listingtype = data.listingtype
entity.latitude = Double(truncating: NSNumber(value: data.latitude))
entity.longitude = Double(truncating: NSNumber(value: data.longitude))
entity.isFavorite = false
}
// saving all pending data at once
do{
try contex.save()
print("success")
}
catch{
print(error.localizedDescription)
}
}
func fetchData(context: NSManagedObjectContext){
let url = "https://... my json"
var request = URLRequest(url: URL(string: url)!)
request.addValue("swiftui2.0", forHTTPHeaderField: "field")
let session = URLSession(configuration: .default)
session.dataTask(with: request) { (data, res, _) in
guard let jsonData = data else{return}
// check for errors
let response = res as! HTTPURLResponse
// checking by status code
if response.statusCode == 404 {
print("error Api Errror")
}
// fetching JSON Data ..
do {
let listings = try JSONDecoder().decode([ListingModel].self, from: jsonData)
DispatchQueue.main.async {
self.listings = listings
self.saveData(contex: context)
}
}
catch {
print(error.localizedDescription)
}
}
.resume()
}
}
I mark posts as favorite with this code:
Button(action: { fetchedData!.isFavorite.toggle();
do {
try context.save()
}
catch {
print(error)
} }) { Image(systemName: fetchedData!.isFavorite ? "heart.circle.fill" : "heart.circle")
On the ContentView i load latest post like this:
HStack {
// checkin if core data exists
if results.isEmpty{
if jsonModel.listings.isEmpty{
HStack(alignment: .center) {
HStack(spacing: 10) {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: Color("dinamicPillsGrass")))
.scaleEffect(2, anchor: .center)
// fetching data
.onAppear(perform: {
jsonModel.fetchData(context: context)
})
}.frame(width: UIScreen.main.bounds.width)
}.modifier(cardHeight())
// when array is clear indicator appears
// as result data is fetched again
}
else{
HStack(spacing: 20) {
ForEach(jsonModel.listings,id: \.self){listing in
NavigationLink (destination: CardDetailView(listing: listing)) {
HStack {
CardViewOrizontal(listing: listing)
}
}.buttonStyle(PlainButtonStyle())
// display fetched Json Data..
}
}
}
}
else{
// results.prefix(?) unde ? cata articole sa arate
HStack(spacing: 20) {
ForEach(results.prefix(10)){listing in
NavigationLink (destination: CardDetailView(fetchedData: listing)) {
HStack {
CardViewOrizontal(fetchedData: listing)
}
}.buttonStyle(PlainButtonStyle())
}
}
.padding(.trailing, 15)
.padding(.leading, 15)
}
// update finish
}.padding(.top, 10)
And i update the data like this:
Note: i also check for Internet connection.
Button(action: {
// clearing data in core data..
if Reachability.isConnectedToNetwork() {
//
do{
jsonModel.listings.removeAll()
**// This code is deleteing all the saved posts - also the favorite ones**
// results.forEach { (listing) in
// context.delete(listing)
// }
**// this code is duplicating only the favorite posts**
results.forEach { (listing) in
if listing.isFavorite.self == false {
context.delete(listing)
jsonModel.listings.removeAll()
}
else {
jsonModel.fetchData(context: context)
}
}
try context.save()
}
catch{
print(error.localizedDescription)
}
print("Network is connected")
self.isError = false
} else {
print("Network is not connected")
self.isError = true
}
}, label: {
HStack(alignment: .center) {
Image(systemName: "icloud.and.arrow.down")
.modifier(textSectionIcon())
Text("Update")
.modifier(textSectionTagline())
}
.padding(5)
.padding(.trailing, 5)
.background(Color("blueLeading"))
.cornerRadius(20)
.modifier(shadowPills())
}).alert(isPresented: $isError) {
Alert(title: Text("Network is not connected"),
message: Text("WiFi or Cellular not availible. You can still browse offline content!"),
dismissButton: .default(Text("OK")))
}
How can i update the fetching withowt duplicating the favorited posts?
i have used
Constrains by id
and introduced:
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
`Now the data is unique, next issue is that the favorites are now getting wipped when i fetch agian!
New question here: Update CoreData when fetch again from online Json

Problems with JSON visualization in SwiftUI App

I'm working on my first app, which loads a JSON with the elements that need to be displayed.
At the moment everything seems to work, except a few details which I hope you can help me solve:
My interface is composed of a TabView contained within a NavigationView (which contains a button that brings up a SheetView) ... if I go to the Tab that brings up the list with the contents downloaded from the JSON everything works, but if I move to another Tab and then I go back to the one that displays the JSON, the data is no longer visible.
I obviously inserted in the code (where necessary) ObservableObject, ObservedObject and onAppear that should reload the JSON, but without success ... it is clear that something is missing ...
How could I go about displaying a list of categories starting from a JSON field? Let me explain, I used the "autore" field of the JSON to define categories, so I would need to first display a list of these categories, then, entering the respective categories, I have to display a list with the elements relating to the selected category and then obviously the DetailView for the single elements.
Of course, since is my first time writing here, ask me everything you need to help me solving the problem...
This is the relevant code:
Code: BussoModel.swift
import SwiftUI
class BussoModel: Identifiable {
var id: UUID = UUID()
var titolo: String = ""
var autore: String = ""
var testo: String = ""
var data: String = ""
var extra1: String = ""
var extra2: String = ""
var fotoUrl: String = ""
}
Code: DownloadManager.swift
import SwiftUI
import Combine
import Alamofire
class DownloadManager: ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
var storage : [BussoModel] = []
func scaricaJSON() {
AF.request("http://geniuspointfrezza.altervista.org/index.php?json=1").responseJSON { response in
if let errore = response.error {
debugPrint(errore.localizedDescription)
return
}
guard let ilJson = response.value else { return }
guard let json = JSON(ilJson).array else { return }
self.storage = []
let totale = json.count
for i in 0..<totale {
let busso = BussoModel()
if let titolo = json[i]["titolo"].string {
busso.titolo = titolo
}
if let autore = json[i]["autore"].string {
busso.autore = autore
}
if let testo = json[i]["testo"].string {
busso.testo = testo
}
if let data = json[i]["data"].string {
busso.data = data
}
if let extra1 = json[i]["extra1"].string {
busso.extra1 = extra1
}
if let extra2 = json[i]["extra2"].string {
busso.extra2 = extra2
}
if let foto = json[i]["fotoUrl"].string {
busso.fotoUrl = foto
}
self.storage.append(busso)
}
self.objectWillChange.send()
}
}
}
Code: CategoryView.swift
import SwiftUI
import URLImage
struct CategoryView: View {
#ObservedObject var dm: DownloadManager
var body: some View {
List {
ForEach(dm.storage) { busso in
NavigationLink(
destination: DetailView(busso: busso)) {
HStack {
URLImage(URL(string: busso.fotoUrl) ?? furl)
.resizable()
.aspectRatio(contentMode: .fit)
.clipped()
.padding()
VStack(alignment: .leading) {
Text(busso.titolo)
.font(.headline)
Text(busso.autore)
.font(.subheadline)
}
Spacer().layoutPriority(-0.1)
}
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 50)
}
}
}
.onAppear {
self.dm.scaricaJSON()
}
}
}
struct CategoryView_Previews: PreviewProvider {
static var previews: some View {
CategoryView(dm: DownloadManager())
}
}
Code: MainTabView.swift
import SwiftUI
struct MainTabView: View {
#State private var selected = 0
var body: some View {
TabView(selection: $selected) {
HomeView()
.tabItem {
Image(systemName: (selected == 0 ? "house.fill" : "house"))
Text("Home")
}.tag(0)
CategoryView(dm: DownloadManager())
.tabItem {
Image(systemName: (selected == 1 ? "text.justify" : "text.justify"))
Text("Categorie")
}.tag(1)
Text("Galleria?")
.tabItem {
Image(systemName: (selected == 2 ? "photo.fill" : "photo"))
Text("Galleria")
}.tag(2)
Text("Preferiti?")
.tabItem {
Image(systemName: (selected == 3 ? "star.fill" : "star"))
Text("Preferiti")
}.tag(3)
}
.accentColor(.white)
.onAppear() {
UINavigationBar.appearance().barTintColor = UIColor(red: 115.0/255.0, green: 90.0/255.0, blue: 143.0/255.0, alpha: 1.0)
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UITabBar.appearance().barTintColor = UIColor(red: 115.0/255.0, green: 90.0/255.0, blue: 143.0/255.0, alpha: 1.0)
}
}
}
struct MainTabView_Previews: PreviewProvider {
static var previews: some View {
MainTabView()
}
}
Code: ContentView.swift
import SwiftUI
import URLImage
let furl = URL(fileURLWithPath: "path")
struct ContentView: View {
#State var showInfoView = false
var body: some View {
NavigationView {
MainTabView()
.sheet(isPresented: $showInfoView) {
InfoView(showInfoView: $showInfoView)
}
.navigationBarTitle((Text("ViviBusso")), displayMode: .inline)
.navigationBarItems(leading:
Button(action: {
debugPrint("QR Code")
}) {
Image(systemName: "qrcode")
}
.foregroundColor(.white), trailing: Button(action: {
self.showInfoView.toggle()
}) {
Image(systemName: "info.circle")
}
.foregroundColor(.white)
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Waiting for suggestions, thanks!
EDIT: New code tested:
import SwiftUI
import URLImage
struct Busso: Codable, Identifiable {
public var id: UUID
public var titolo: String
public var autore: String
public var testo: String
public var data: String
public var extra1: String
public var extra2: String
public var foto: String
public var fotoUrl: String
}
class FetchBusso: ObservableObject {
#Published var Bussos = [Busso]()
init() {
let url = URL(string: "https://geniuspointfrezza.altervista.org/index.php?json=1")!
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
if let bussoData = data {
let decodedData = try JSONDecoder().decode([Busso].self, from: bussoData)
DispatchQueue.main.async {
self.Bussos = decodedData
}
} else {
print("No data")
}
} catch {
print("Error")
}
}.resume()
}
}
struct CategoryView: View {
#ObservedObject var fetch = FetchBusso()
var body: some View {
VStack {
List(fetch.Bussos) { todo in
VStack(alignment: .leading) {
Text(todo.titolo)
}
}
}
}
}
struct CategoryView_Previews: PreviewProvider {
static var previews: some View {
CategoryView()
}
}

Swift ui macos sort json by obj

I make a json call to get some data from as seen from the image, but I have to sort these items by stargazers_count, ie the items with the largest stargazers_count put before.
Can you give me a hand?
Code:
import SwiftUI
import AppKit
struct Obj: Codable, Identifiable {
public var id: Int
public var name: String
public var language: String?
public var description: String?
public var stargazers_count: Int
public var forks_count: Int
}
class Fetch: ObservableObject {
#Published var results = [Obj]()
init(name: String) {
let url = URL(string: "https://api.github.com/users/"+name+"/repos?per_page=1000")!
URLSession.shared.dataTask(with: url) { data, response, error in
do {
if let data = data {
let results = try JSONDecoder().decode([Obj].self, from: data)
DispatchQueue.main.async {
self.results = results
}
print("Ok.")
} else {
print("No data.")
}
} catch {
print("Error:", error)
}
}.resume()
}
}
struct ContentViewBar: View {
#ObservedObject var fetch = Fetch(name: "github")
var body: some View {
VStack(alignment: .leading, spacing: 0) {
List(fetch.results) { el in
VStack(alignment: .leading, spacing: 0) {
Text("\(el.name) (\(el.stargazers_count)/\(el.forks_count)) \(el.language ?? "")").padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0))
if el.description != nil {
Text("\(el.description ?? "")")
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}.listStyle(SidebarListStyle())
// Button(action: { NSApplication.shared.terminate(self) }){
// Text("X")
// .font(.caption)
// .fontWeight(.semibold)
// }
// .padding(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 2))
// .frame(width: 360.0, alignment: .trailing)
}
.padding(0)
.frame(width: 360.0, height: 360.0, alignment: .top)
}
}
struct ContentViewBar_Previews: PreviewProvider {
static var previews: some View {
ContentViewBar()
}
}
In the end, he seems to have succeeded.
Code:
List(fetch.results.sorted { $0.stargazers_count > $1.stargazers_count }) { el in
Sorting in UI drawing cycle might result in performance issue (especially in case of huge container), so it is better to perform sort either in-advance, or out-of-UI flow.
So I'd recommend to do it here
let results = try JSONDecoder().decode([Obj].self, from: data)
let sorted = results.sorted { $0.stargazers_count > $1.stargazers_count }
DispatchQueue.main.async {
self.results = sorted
}

Toggle sorting in List View

I use 'Api' as an ObervableObject and inside there's #Published var isAscd to be toggled. Function getPost fetch JSON data from web and populate is List View. I use a button in HeaderView to toggle the sorting method. Compiling is succeeded but button takes no action. I don't know what goes wrong?
class Api: ObservableObject {
#Published var posts: [Post] = []
#Published var isAscd: Bool = false
func getPosts(completion: #escaping ([Post]) -> ()) {
guard let url = URL(string: getListURLString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
let posts = try! JSONDecoder().decode([Post].self, from: data!)
DispatchQueue.main.async {
completion(posts)
}
}
.resume()
}
}
struct HeaderView: View {
var holding: String = "市值/數量"
var earning: String = "現價/成本"
var profit: String = "持倉賺蝕"
#ObservedObject var api = Api()
var body: some View {
HStack{
Button(action: {
self.api.isAscd.toggle()
if self.api.isAscd {
self.api.posts.sort { $0.stockcode > $1.stockcode}
} else { self.api.posts.sort { $0.stockcode < $1.stockcode}
}
print(self.api.posts.count)
print(self.api.isAscd)
}.......}
List {
Section(header: HeaderView())
{
ForEach(posts)
{ post in
HStack {
VStack(alignment: .leading)
..........
}
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 250, alignment: .center)
.onAppear {
Api().getPosts {(posts) in
self.posts = posts
//self.apr.isAscd.toggle()
}
}
}