How can I connect a SwiftUI TextField to a NSManagedObject property? - nsmanagedobject

I want to create a view allowing the user to edit a single NSManagedObject entity like below.
class Employee: NSManagedObject {
#NSManaged var name: String
}
struct EditEmpView : View {
#ObservedObject var empToEdit: Employee
var body: some View {
TextField("Name", text: $empToEdit.name)
}
}

Related

SwiftUI parse a json from an URL with entry values

I'm stuck when a I need to load a json file to swiftUI when this json comes from an URL with entry variables that must be filled by the user before getting the data.
I have the following code:
// This is the structure to get the data from the API.
struct loka_result: Codable {
var date: String
var time: String
var unix_time: Int
var seqNumber : Int
var lat: Double
var lng: Double
var device: String
var accuracy: Double
var info: String
var temperature : Double?
var battery_voltage : Float?
}
// Now conform to Identifiable
extension loka_result: Identifiable {
public var id: Int { return unix_time }
}
// Model Data to get the data
final class ModelData: ObservableObject {
#Published var allthelokas : [loka_result] = loadAllData(deviceId:selected_loka, timeDelta: selected_delta)
}
// Function to get and parse the data
func loadAllData<T: Decodable>(deviceId:String,timeDelta:Int) -> T
{
let url = URL(string: "https://xxxx.php?device=\(deviceId)&hours=\(timeDelta)"),
data = try? Data(contentsOf: url!)
return
try! JSONDecoder().decode(T.self, from: data!)
}
Then in TempChartView I would like to get a list
import SwiftUI
struct TempChartNew: View {
#EnvironmentObject var modelData: ModelData
var body: some View {
VStack {
List(modelData.allthelokas) { j in
// ForEach(modelData.allthelokas) { jj in
HStack {
Text(j.device)
Text(j.date)
Text(j.time)
Text(String(j.seqNumber))
}
}
}
}
}
struct TempChartNew_Previews: PreviewProvider {
static var previews: some View {
TempChartNew().environmentObject(ModelData())
}
}
all this its working because just to test it I have added two global variables to fill in the data for the function.
var selected_loka: String = "3JJJ30"
var selected_delta: Int = 24
But the ideia would be for the view to have two entry points.
And its here where I'm stuck ! I don't know how to pass the variables to the class.
Does anyone can please point me in the correct direction ? - Thank you

SwiftUI: CoreData and complex JSON

I'm trying to save JSON to CoreData but the JSON is a little bit complex with many array objects. I could only save and display simple attributes like String, Int... but if I add attributes with Transformable type like [Currency], [Double], the app will crash.
Could you please show me the right way to implement this?
My Data Model
struct Currency: Decodable {
let code, name, symbol: String?
}
struct Language : Decodable {
let name : String?
}
struct WorldData: Identifiable, Decodable {
var id : Int {
return population
}
var name : String?
...
var latlng : [Double]?
var currencies : [Currency]?
var languages : [Language]
...
}
My Manual NSManagedObject subclass
extension CDCountry {
#nonobjc public class func fetchRequest() -> NSFetchRequest<CDCountry> {
return NSFetchRequest<CDCountry>(entityName: "CDCountry")
}
#NSManaged public var name: String?
#NSManaged public var latlng: NSObject?
#NSManaged public var currencies: NSObject?
#NSManaged public var languages: NSObject?
...
public var wrapperLatlng : NSObject {
latlng ?? ["Unknown error"] as NSObject
}
public var wrapperCurrencies : NSObject {
currencies ?? ["Unknown error"] as NSObject
}
...
and how I save loaded JSOn to CoreData
static func loadDataToCD(moc: NSManagedObjectContext) {
loadDataFromJSON { (countries) in
DispatchQueue.main.async {
var tempCountries = [CDCountry]()
for country in countries {
let newCountry = CDCountry(context: moc)
...
newCountry.currencies = country.currencies as NSObject?
newCountry.languages = country.languages as NSObject
newCountry.latlng = country.latlng as NSObject?
newCountry.name = country.name
...
tempCountries.append(newCountry)
}
do {
try moc.save()
} catch let error {
print("Could not save to CD: ", error.localizedDescription)
}
}
There is no easy way to answer your question but I can point you to some resources.
There is a good example on decoding JSON within the Landmark.swift file included in the "Handing User Input" Tutorial that Apple provides for SwiftUI. That might help with the basics. https://developer.apple.com/tutorials/swiftui/handling-user-input
The tutorial uses custom Coordinates and Category classes that can likely mimic the process for your Currency and Language objects. But of course that would be for a Non-Managed Object.
To do it Managed it would be best to create CoreData objects for Currency and Language and join them as a relationship in CoreData vs making them a Transformable.
Then you can follow the answer for this older question that was answered very throughly Save complex JSON to Core Data in Swift
You can also look at this question Swift And CoreData With Custom Class As Transformable Object

ObservedObject, derived from Json, not loading into Picker, but loads in List. In SwiftUI, Xcode 11

I'm new to mobile development, and in the process of learning SwiftUI.
I've been struggling to figure out what's wrong with my picker. I am successfully returning the data from my URLSession, adding it to my model. I can confirm this by adding my #ObservedObject to a List, which returns all of the items. Putting the same #ObservedObject into a picker returns an empty picker for some reason. Any help would be appreciated. Thanks.
Here's my view with the Picker(). When run, the Picker is empty. I can comment out the Picker(), leaving just the ForEach() with Text(), and the text appears.
import Foundation
import Combine
import SwiftUI
struct ContentView: View {
#ObservedObject var countries = CulturesViewModel()
#State private var selectedCountries = 0
var body: some View {
VStack {
//loop through country array and add them to picker
Picker(selection: $selectedCountries, label: Text("Select Your Country")) {
ForEach(0 ..< countries.cultures.count, id: \.self) { post in
Text(self.countries.cultures[post].Culture).tag(post)
}
}.labelsHidden()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here's my ViewModel. It set's the #Published variable to the results of the JSON request in the WebService(). If I hard-code the #Published variable to the value that's begin returned, the Picker works.
import Foundation
import Combine
import SwiftUI
class CulturesViewModel: ObservableObject {
init() {
fetchCultures()
}
#Published var cultures = [Culture](){
didSet {
didChange.send(self)
}
}
private func fetchCultures(){
WebService().GetCultures {
self.cultures = $0
}
}
let didChange = PassthroughSubject<CulturesViewModel, Never>()
}
Here's my WebService(). Unfortunately, I'm unable to share the JSON url, I've added in the json that's returned.
import Foundation
import SwiftUI
import Combine
class WebService {
func GetCultures(completion: #escaping([Culture]) ->()) {
guard let url = URL("")
[
{
"CultureId": 0,
"Culture": "Select Your Country"
},
{
"CultureId": 1078,
"Culture": "English (United States)"
},
{
"CultureId": 6071,
"Culture": "English (Canada)"
}
]
URLSession.shared.dataTask(with: url) { (data,_,_) in
do {
if let data = data {
let culturesList = try JSONDecoder().decode([Culture].self, from: data)
DispatchQueue.main.async {
completion(culturesList)
}
} else {
DispatchQueue.main.async {
completion([])
}
}
} catch {
print(error)
DispatchQueue.main.async {
completion([])
}
}
}.resume()
}
}
Lastly, here's my Model.
import Foundation
struct Culture: Codable, Identifiable {
var id = UUID()
var CultureId: Int
var Culture: String
}
The work around to make the picker refresh is to add a unique id. Refreshing (or) reloading the countries, will create a new UUID for the picker items. This will force the picker to refresh. I've modified your code to include an id.
//loop through country array and add them to picker
Picker(selection: $selectedCountries, label: Text("Select Your Country")) {
ForEach(0 ..< countries.cultures.count, id: \.self) { post in
Text(self.countries.cultures[post].Culture).tag(post)
}
}.labelsHidden()
.id(UUID())
This seems to be a known issue with the picker.

Type '_' has no member 'id' SwiftUI

So over the last couple hours, I've been trying to create a horizontal card stack that will show the most recent headlines for a project I am working on. Where I am at now, is holding up my entire flow, because my list cannot find my id variable in my results struct. Below I've attached the code, and any help would be appreciated.
HeadlinesUI.swift - Screenshot
import SwiftUI
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var id: Int
var title: String
var header_image: String
var summary: String
}
struct HeadlineUI: View {
#State var results = [Result]()
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 20) {
List(results, id: \.id) { items in
}
.onAppear(perform: loadData)
}
}
}
func loadData() {
}
}
struct HeadlineUI_Previews: PreviewProvider {
static var previews: some View {
HeadlineUI()
}
}
As mentioned in the comments Result is a type in Swift, so you have to use a different name.
I went for:
struct MyResult: Codable {
var id: Int
var title: String
var header_image: String
var summary: String
}
since it has got an id you can make it Identifiable:
extension MyResult: Identifiable {}
as for the body:
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 20) {
ForEach(results) { result in
Text(result.title)
}
.onAppear(perform: { self.loadData() })
}
}
}
You rather need a ForEach then a List and the builder cannot be empty. Also onAppear takes a closure as its argument. Once you make all the changes the View will behave as expected.

How to populate my object Array with JSON data using Alamofire

I am new to Swift and I'm trying to creat a mobile app for my woocommerce store. I created a product class with name, price and image src. I also created another productBank to store my product objects but I don't know how to populate it with JSON using Alamofire.
class Simpleproduct {
var productName: String
var productPrice: Int
var ProductImage: String
init(price: Int, name: String, image: String) {
productName = name
productPrice = price
ProductImage = image
}
}
class Productbank {
var productlist = [Simpleproduct]()
init() {
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
// [...]
Posting my answer again because it was deleted...
I recommend using AlamoFireObjectMapper, is a cool and easy to use library:
https://github.com/tristanhimmelman/AlamofireObjectMapper
your code will be something like this (you will receive the json automatically mapped to your object model):
The object Model
class EncuestaModel:Object, Mappable{
#objc dynamic var Id = 0
#objc dynamic var Name:String?
#objc dynamic var CreationDate:Date?
#objc dynamic var Status = 0
#objc dynamic var Default = false
required convenience init?(map: Map) {
self.init()
}
}
and for the Alamofire request:
func getEncuestas(){
let url = "yourApiUrl"
Alamofire.request(url).responseObject {(response:DataResponse<LoginResultModel>) in
if let result = response.result.value {
//do your code here
}
}
}