Swift 3 - Declaring a simulated JSON as a response from a server - json

I'm making an application with swift 3.0. But I have a problem, because in the API REST still have not implemented the service, I'm creating a simulated JSON to continue working. But the problem as you will see at the end of all the explanation in the image is that I do not know how to declare a JSON "-.- .... Basically the program will make a call to the server and it will respond with a JSON (now I pass it "the simulated" you will see it in the code). And with that JSON maps it with ObjectMapper to some models (that I pass the code) so that in the end the application has an object.
Error declaring Simulated JSON
These are the three models I have to map the JSON when it will come from the server or in this case, the simulated JSON.
The first is "LegendEntriesModel":
import Foundation
import ObjectMapper
import AlamofireDomain
class LegendEntriesModel: Mappable {
fileprivate var _id_snapshot: String?
fileprivate var _date: String?
fileprivate var _deliverables: [DeliverablesModel]?
init(){}
required init?(map: Map) { }
func mapping(map: Map) {
self.id_snapshot <- map["id_snapshot"]
self.date <- map["date"]
self.deliverables <- map["deliverables"]
}
var id_snapshot: String {
get {
if _id_snapshot == "" {
_id_snapshot = ""
}
return _id_snapshot!
}
set {
_id_snapshot = newValue
}
}
var date: String {
get {
if _date == "" {
_date = ""
}
return _date!
}
set {
_date = newValue
}
}
var deliverables: [DeliverablesModel] {
get {
if _deliverables == nil {
_deliverables = []
}
return _deliverables!
}
set {
_deliverables = newValue
}
}
//MARK: RELEASE MEMORY BETWEEN OBJECT AND API REST (BROKE DEPENDENCIS)
func copy()->LegendEntriesModel {
let legendEntriesModel = LegendEntriesModel()
legendEntriesModel.id_snapshot = self.id_snapshot
legendEntriesModel.date = self.date
legendEntriesModel.deliverables = copyDeliverables()
return legendEntriesModel
}
func copyDeliverables() -> [DeliverablesModel]{
var newArray: [DeliverablesModel] = []
for item in deliverables {
newArray.append(item.copy())
}
return newArray
}
}
The second on is "DeliverablesModel"
import Foundation
import ObjectMapper
import AlamofireDomain
class DeliverablesModel: Mappable {
fileprivate var _id: String?
fileprivate var _type: String?
fileprivate var _url_layer: String?
fileprivate var _options: OptionsDeliverablesModel?
init(){}
required init?(map: Map) { }
func mapping(map: Map) {
self.id <- map["id"]
self.type <- map["type"]
self.url_layer <- map["url_layer"]
self.options <- map["options"]
}
var id: String {
get {
if _id == "" {
_id = ""
}
return _id!
}
set {
_id = newValue
}
}
var type: String {
get {
if _type == "" {
_type = ""
}
return _type!
}
set {
_type = newValue
}
}
var url_layer: String {
get {
if _url_layer == "" {
_url_layer = ""
}
return _url_layer!
}
set {
_url_layer = newValue
}
}
var options: OptionsDeliverablesModel {
get {
if _options == nil {
_options = OptionsDeliverablesModel()
}
return _options!
}
set {
_options = newValue
}
}
//MARK: RELEASE MEMORY BETWEEN OBJECT AND API REST (BROKE DEPENDENCIS)
func copy()->DeliverablesModel {
let deliverablesModel = DeliverablesModel()
deliverablesModel.id = self.id
deliverablesModel.type = self.type
deliverablesModel.url_layer = self.url_layer
deliverablesModel.options = self.options
return deliverablesModel
}
}
And the last one is "OptionsDeliverablesModel":
import Foundation
import ObjectMapper
import AlamofireDomain
class OptionsDeliverablesModel: Mappable {
fileprivate var _type: String?
fileprivate var _max_range: Float?
fileprivate var _min_range: Float?
fileprivate var _title: String?
fileprivate var _initial_max_value: Float?
fileprivate var _initial_min_value: Float?
fileprivate var _id: String?
init(){}
required init?(map: Map) { }
func mapping(map: Map) {
self.type <- map["type"]
self.max_range <- map["max_range"]
self.min_range <- map["min_range"]
self.title <- map["title"]
self.initial_max_value <- map["initial_max_value"]
self.initial_min_value <- map["initial_min_value"]
self.id <- map["id"]
}
var type: String {
get {
if _type == "" {
_type = ""
}
return _type!
}
set {
_type = newValue
}
}
var max_range: Float {
get {
if _max_range == 0 {
_max_range = 0
}
return _max_range!
}
set {
_max_range = newValue
}
}
var min_range: Float {
get {
if _min_range == 0 {
_min_range = 0
}
return _min_range!
}
set {
_min_range = newValue
}
}
var title: String {
get {
if _title == "" {
_title = ""
}
return _title!
}
set {
_title = newValue
}
}
var initial_max_value: Float {
get {
if _initial_max_value == 0 {
_initial_max_value = 0
}
return _initial_max_value!
}
set {
_initial_max_value = newValue
}
}
var initial_min_value: Float {
get {
if _initial_min_value == 0 {
_initial_min_value = 0
}
return _initial_min_value!
}
set {
_initial_min_value = newValue
}
}
var id: String {
get {
if _id == "" {
_id = ""
}
return _id!
}
set {
_id = newValue
}
}
//MARK: RELEASE MEMORY BETWEEN OBJECT AND API REST (BROKE DEPENDENCIS)
func copy()->OptionsDeliverablesModel {
let optionsDeliverablesModel = OptionsDeliverablesModel()
optionsDeliverablesModel.type = self.type
optionsDeliverablesModel.max_range = self.max_range
optionsDeliverablesModel.min_range = self.min_range
optionsDeliverablesModel.title = self.title
optionsDeliverablesModel.initial_max_value = self.initial_max_value
optionsDeliverablesModel.initial_min_value = self.initial_min_value
optionsDeliverablesModel.id = self.id
return optionsDeliverablesModel
}
}
With these three "Models" are what I can map the JSON inside the class DAO, but here is the problem, because I do not know how to pass my JSON that I have simulated.
The code is as follows:
import AlamofireDomain
import Alamofire
import ObjectMapper
class DeliverablesLegendDAO : SimpleDAO {
var deliverables = Dictionary<String, Any>()
deliverables = [{"legendEntries": [{"id_snapshot": "123","date": "2016-10-20","deliveries": [{"id": "12","type": "RGB","url_layer":"topp:states","options": [{"type": "range","max_range": 100,"min_range": 0,"title": "Option RGB","initial_max_value": 100,"initial_min_value": 0,"id": "depth"}]}]}]}]
func snapshots(_ parameters: String,
callbackFuncionOK: #escaping (LegendEntriesModel)->(),
callbackFunctionERROR: #escaping (Int,NSError)->()) {
Alamofire.request(parameters,
method: .post,
encoding: JSONEncoding.default)
.responseJSON { response in
if response.result.isSuccess{
if let status = response.response?.statusCode {
switch(status){
case 200:
let value = response
let legendEntries = Mapper<LegendEntriesModel>().map(JSONObject: value)
callbackFuncionOK(legendEntries!)
default:
break
}
}
}
else {
var statusCode = -1
if let _response = response.response {
statusCode = _response.statusCode
}
var nsError: NSError = NSError(domain: Constants.UNKNOWN_HTTP_ERROR_MSG,
code: Constants.UNKNOWN_HTTP_ERROR_ID,
userInfo: nil)
if let _error = response.result.error {
nsError = _error as NSError
}
callbackFunctionERROR(statusCode,nsError)
}
}
}
}
As you can see in the image, I am declaring my simulated JSON wrong and then map it with "LegendDeliveriesModel" to an object. How can I do it?
Error declaring simulated JSON
If you need anything else, tell me. I repeat, the problem is in the JSON simulated statement that I do not know how to pass it to DAO and that it maps it.

Hi not sure if you will be open to this, but it will be better to try creating a JSON in file and load it in using Bundle like this :
func loadJsonFrom(fileName: String) -> NSDictionary {
let path = Bundle.main.path(forResource: filename, ofType: "json")
let jsonData = try! Data(contentsOf: URL(fileURLWithPath: path!))
let jsonResult: NSDictionary = try! JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as! NSDictionary
return jsonResult
}

I think your syntax is wrong for declaring your JSON. Pretty sure declaring Dictionaries inline in swift you only use ["key":"value"]
So just remove all of the { and }
Edit: Sorry, didn't realise it was outside of a method. If you want to do that you have to declare it directly like so
var deliverables = ["legendEntries": ["id_snapshot": "123","date": "2016-10-20","deliveries": ["id": "12","type": "RGB","url_layer":"topp:states","options": ["type": "range","max_range": 100,"min_range": 0,"title": "Option RGB","initial_max_value": 100,"initial_min_value": 0,"id": "depth"]]]]
If you're just using it as mock Data I would also consider making it a let constant rather than a variable

Related

SwiftUI: Instance member 'enableFilter' cannot be used on type 'ContentView'; did you mean to use a value of this type instead?

I'm a beginner with SwiftUI and I wanted to create a dynamic list with the birthdays of people. I also wanted to integrate a filter that facilitates to find birthdays. But in the lines:
if enableFilter == true {
return json.filter {$0.BirthdayString.contains(filter(date: filterDate))}
} else {
return json
}
I always get these errors:
Instance member 'enableFilter' cannot be used on type 'ContentView';
did you mean to use a value of this type instead?
and
Instance member 'filterDate' cannot be used on type 'ContentView'; did
you mean to use a value of this type instead?
I think I understand why the errors are present but I don't no how to fix it. I tried:
#State static var
but then I cannot change the values with my
filterView
Thank you for your help, here is the full source code:
import SwiftUI
struct person: Codable, Hashable, Identifiable {
var id: Int
var Birthday: Date
var BirthdayString: String
}
func filter(date: Date) -> String {
let DateComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
let DateComponentsString: String = "\(DateComponents.day)/\(DateComponents.month)/\(DateComponents.year)"
return DateComponentsString
}
struct ContentView: View {
#State var people: [person] = {
guard let data = UserDefaults.standard.data(forKey: "people") else { return [] }
if let json = try? JSONDecoder().decode([person].self, from: data) {
if enableFilter == true {
return json.filter {$0.BirthdayString.contains(filter(date: filterDate))}
} else {
return json
}
}
return []
}()
#State var filterDate: Date = Date()
#State var enableFilter: Bool = false
#State var showFilter: Bool = false
#State var newPersonDate: Date = Date()
var body: some View {
NavigationView {
VStack {
HStack {
DatePicker(selection: $newPersonDate, label: {Text("Birthday")}).padding()
Button(action: {didTapAddTask()}, label: {Text("Add")}).padding()
}
List {
ForEach($people) { $person in
Text("\(person.Birthday)")
}
}
}
.navigationTitle(Text("People's birthday"))
}
}
var filterView: some View {
VStack {
DatePicker(selection: $filterDate, label: {Text("Date")}).padding()
Toggle(isOn: $enableFilter, label: {Text("enable filter")}).padding()
}
}
func didTapAddTask() {
let id = people.reduce(0) { max($0, $1.id) } + 1
people.insert(person(id: id, Birthday: newPersonDate, BirthdayString: filter(date: newPersonDate)), at: 0)
newPersonDate = Date()
save()
}
func save() {
guard let data = try? JSONEncoder().encode(people) else { return }
UserDefaults.standard.set(data, forKey: "people")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can use another Computed property which will get the data based on some another property's value. i.e enableFilter.
#State var people: [person] = {
guard let data = UserDefaults.standard.data(forKey: "people") else { return [] }
if let json = try? JSONDecoder().decode([person].self, from: data) {
return json
}
return []
}()
var data : [person] {
if enableFilter {
return people.filter {$0.BirthdayString.contains(filter(date: filterDate))}
} else {
return people
}
}
And use this property to get the relevant data:
var body: some View {
NavigationView {
VStack {
HStack {
DatePicker(selection: $newPersonDate, label: {Text("Birthday")}).padding()
Button(action: {didTapAddTask()}, label: {Text("Add")}).padding()
}
List {
ForEach(data) { person in // <<--- Here `data`
Text("\(person.Birthday)")
}
}
}
.navigationTitle(Text("People's birthday"))
}
}

How to return object from nested json

I have nested JSON and I'm trying to return an object and update UI. However, I can access properties only from Result array, but can't reach Step from AnalyzedInstactions.
Here are classes that represent data in the JSON. Generated it with https://app.quicktype.io
import UIKit
class ResultArray: Codable {
var results = [Result]()
}
class Result: Codable, CustomStringConvertible {
var title: String = ""
var image = ""
var readyInMinutes: Int? = 0
var servings: Int? = 0
var cuisines = [String]()
var dishTypes = [String]()
var diets = [String]()
var occasions = [String]()
var analyzedInstructions = [AnalyzedInstruction]()
var description: String {
return "\nResults - Name: \(title), Summary: \(String(describing: readyInMinutes ?? nil)), \(String(describing: servings ?? nil)) "
}
}
// MARK: - Steps
class AnalyzedInstruction: Codable {
var name: String? = ""
var steps = [Step]()
}
class Step: Codable {
var number: Int = 0
var step: String = ""
}
Here is my parse method
private func parse(data: Data) -> [Result] {
do {
let decoder = JSONDecoder()
let result = try decoder.decode(ResultArray.self, from: data)
return result.results
} catch {
print("JSON Error: \(error)")
return []
}
}
try this simple code to access your steps from AnalyzedInstructions:
let results: [Result] = parse(data: theData)
if let firstResult = results.first {
if let firstAnaInst = firstResult.analyzedInstructions.first {
for step in firstAnaInst.steps {
print("--> step.step: \(step.step) step.number: \(step.number)")
}
} else { print(" NO analyzedInstructions") }
} else { print(" NO results") }
if you want all steps:
for result in results {
for anaInst in result.analyzedInstructions {
for step in anaInst.steps {
print("--> step.step: \(step.step) step.number: \(step.number)")
}
}
}
PS: if appropriate, you may consider using struct instead of class for your json models.

How to fetch JSON data in background Swiftui

I have a JSON API I want my app to download in background.
My Quotes app send out notifications with timeintervals and I want the quotes to be in the notification.
struct Quotetype: Codable {
let text: String?
let author: String?
}
class ViewModel: ObservableObject {
#Published var quotes: [Quotetype]?
#Published var isLoading: Bool = true
var quoteText: String = ""
var quoteAuthor: String = ""
init() {
fetchQuotes()
setUpNotificationPermission()
setUpNotificationTriggers()
}
func fetchQuotes() {
guard let url = URL(string: "https://type.fit/api/quotes") else { return }
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
if error == nil {
if let data = data {
do {
let jsonContent = try JSONDecoder().decode([Quotetype].self, from: data)
DispatchQueue.main.async { [self] in
self?.quotes = jsonContent
if self?.quotes?.count != nil {
let randomNumber = Int.random(in: 0..<self!.quotes!.count )
self?.isLoading = false
self?.quoteText = self!.quotes?[randomNumber].text ?? ""
self?.quoteAuthor = self!.quotes?[randomNumber].author ?? ""
}
}
} catch {
print("\(error)")
}
} else {
print("Data nil")
}
} else {
print("err \(String(describing: error))")
}
}.resume()
}
func setUpNotificationTriggers() {
let content = UNMutableNotificationContent()
content.title = "Quote of the Day"
content.subtitle = quoteText
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
this is my fetch & Notification. How can I get the quoteText in background so notification can go through?
please provide with examples.

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()
}
}

how to do safe json parsing in swift?

I am getting a Json of images from sever i have created one modal class & under which i have declared images as member of that class.
class SomeClass: NSObject {
var objImages:[Images] = [Images]()
}
Images as class
class Images {
var thumbImage:String = ""
var fullImage:String = ""
init(dictionary:NSDictionary){
fullImage = dictionary["thumb"] as? String ?? ""
thumbImage = dictionary["full_url"] as? String ?? ""
}
init() {
}
}
Parsing the json data
if let arrImg = dictionary["images"] {
for value in arrImg as! NSArray {
let tempImage:Images = Images(dictionary: value as! NSDictionary)
recipeImages.append(tempImage)
}
}
below is the josn response
{ "images": [
{
"thumb": "https://mysevrer.com/v0/b/a.png”,
"full_url": "https://mysevrer.com/v0/b/b.png"
},
{
"thumb": "https://mysevrer.com/v0/b/a.png”",
"full_url": "https://mysevrer.com/v0/b/b.png”"
}
]
}
Please is it safe way to parse response ?
What if i don'tget image as array
What if i don't get the "thumb"
& "full" as keys
I'm not sure if this is what you mean, but this will return an optional instance of Images. So it will return nil if the dictionary doesn't contain one or both of those keys.
class Images {
var thumbImage:String
var fullImage:String
init?(dictionary:Dictionary<String,String>){
guard let image = dictionary["thumb"] else { return nil }
guard let thumb = dictionary["full_url"] else { return nil }
self.fullImage = image
self.thumbImage = thumb
}
}
For the parsing part of the array.
guard let imagesArray = dictionary["images"] as? Array<Dictionary<String,String>> else { return }
for dict in imagesArray {
guard let images = Images(dictionary: dict) else { continue }
recipeImages.append(images)
}