Save complex relational data in CoreData - swift

I'm back on a learning course in SwiftUI using CoreData. I have three entities:
User // Has many Customers
Customer // Belongs to User and has many PartExchanges
PartExchange // Belongs to customer
When user first installs the app after they logged in, I fetch some initial data to be saved (the above: customers, part exchnages etc...):
struct AuthResponse: Decodable {
let error: String?
let token: String?
let userData: UserObject?
let customers: [Customers]?
struct UserObject: Decodable {
let FirstName: String?
let Surname: String?
let EmailAddress: String?
let UserID: String
}
struct Customers: Decodable {
let FirstName: String?
let Surname: String?
let EmailAddress: String?
let Customer_ID: String
let PartExchanges: [PartExchangeData]?
}
}
// In another file and not inside AuthResponse
struct PartExchangeData: Decodable {
let Registration: String?
let Customer_ID: String?
let PartExchange_ID: String?
let Variant: String?
let Colour: String?
}
AuthResponse is only used when user first logs in or reinstalls the app to get the initial data from our API:
// The exact data I have
import SwiftUI
class AuthController {
var emailUsername: String = ""
var password: String = ""
func login() -> Void {
guard let url = URL(string: "http://localhost:4000/api/auth") else {
print("Invalid URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: AnyHashable] = [
"emailUsername": emailUsername,
"password": password
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
// Make the request
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(AuthResponse.self, from: data) {
DispatchQueue.main.async {
if decodedResponse.error != nil {
// Tell user?
return
}
let userObject = UserModel()
userObject.createUser(authObject: decodedResponse)
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
Last, the UserModel:
class UserModel: ObservableObject {
private let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
private let viewContext = PersistenceController.shared.container.viewContext
#Published var saved: Bool = false
var firstName: String = ""
var surname: String = ""
var emailAddress: String = ""
var token: String = ""
var userId: String = ""
init() {...}
public func createUser(authObject: AuthResponse) -> Void {
do {
// Create a user on first login
let user = User(context: viewContext)
let customer = Customer(context: viewContext)
let partExchange = PartExchange(context: viewContext)
//let userCustomers: [AuthResponse.Customers]
user.firstName = authObject.userData!.FirstName
user.surname = authObject.userData!.Surname
user.emailAddress = authObject.userData!.EmailAddress
user.token = authObject.token!
user.userId = authObject.userData!.UserID
// Save customers
for cus in authObject.customers! {
customer.firstName = cus.FirstName
customer.surname = cus.Surname
user.addToCustomers(customer)
// save part exchanges
for px in cus.PartExchanges! {
partExchange.registration = px.Registration
partExchange.partExchangeId = px.PartExchange_ID
partExchange.variant = px.Variant
customer.addToPartExchanges(partExchange)
}
}
try viewContext.save()
saved = true
print("ALL SAVED!!")
} catch {
let error = error as NSError
// If any issues, rollback? viewContext.rollback()
fatalError("Could not save user: \(error)")
}
}
public func logOut() {
// Only remove the token....
}
}
The issue I'm having with this approach is when saving; it's saving the last customer in the loop.
Xcode generated some extensions for User, Customer and PartExchnage and inside User, I see a function: #NSManaged public func addToCustomers(_ values: NSSet):
[..]
user.addToCustomers(<what-goes-here>)
My User entity saves correctly. Customer only has the last data from the api array. How to correctly save the user with many customers, where the each customer has many part exchanges?

You need to create a new object for each iteration in each of your loops since each object created will be stored as a separate item in Core Data
So change createUser like this
public func createUser(authObject: AuthResponse) -> Void {
do {
let user = User(context: viewContext)
user.firstName = authObject.userData!.FirstName
// more properties ...
for cus in authObject.customers! {
let customer = Customer(context: viewContext)
customer.firstName = cus.FirstName
customer.surname = cus.Surname
user.addToCustomers(customer)
for px in cus.PartExchanges! {
let partExchange = PartExchange(context: viewContext)
partExchange.registration = px.Registration
partExchange.partExchangeId = px.PartExchange_ID
partExchange.variant = px.Variant
customer.addToPartExchanges(partExchange)
}
}
try viewContext.save()
saved = true
print("ALL SAVED!!")
} catch let error = error as NSError {
//Either log the error and return some status or throw it
//FatalError is a bit to much in this situation
fatalError("Could not save user: \(error)")
}
}

Related

Thread 1: EXC_BAD_INSTRUCTION when fetching data

I get this Error -> Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) randomly. I don't quite understand when exactly it happens. Most of the times it is when the view refreshes. The Error appears at the line where group.leave() gets executed.
What am I trying to do:
I want to fetch albums with their image, name and songs that also have a name and image from my firebase database. I checked for the values and they're all right as far as I can tell. But when trying to show them it is random what shows. Sometimes everything is right, sometimes one album gets showed twice, sometimes only one album gets showed at all, sometimes one album has the songs of the other album.
My firebase database has albums stored as documents, each document has albumimage/name and 2 subcollections of "unlocked" with documents(user uid) that store "locked":Bool and "songs" with a document for each song that stores image/name
This is the function that fetches my albums with their songs:
let group = DispatchGroup()
#State var albums: [Album] = []
#State var albumSongs: [AlbumSong] = []
func fetchAlbums() {
FirebaseManager.shared.firestore.collection("albums").getDocuments { querySnapshot, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let documents = querySnapshot?.documents else {
return
}
let uid = FirebaseManager.shared.auth.currentUser?.uid
documents.forEach { document in
let data = document.data()
let name = data["name"] as? String ?? ""
let artist = data["artist"] as? String ?? ""
let releaseDate = data["releaseDate"] as? Date ?? Date()
let price = data["price"] as? Int ?? 0
let albumImageUrl = data["albumImageUrl"] as? String ?? ""
let docID = document.documentID
FirebaseManager.shared.firestore.collection("albums").document(docID)
.collection("songs").getDocuments { querySnapshot, error in
if let error = error {
return
}
guard let documents = querySnapshot?.documents else {
return
}
self.albumSongs = documents.compactMap { document -> AlbumSong? in
do {
return try document.data(as: AlbumSong.self)
} catch {
return nil
}
}
group.leave()
}
FirebaseManager.shared.firestore.collection("albums").document(docID)
.collection("unlocked").document(uid ?? "").getDocument { docSnapshot, error in
if let error = error {
return
}
guard let document = docSnapshot?.data() else {
return
}
group.enter()
group.notify(queue: DispatchQueue.global()) {
if document["locked"] as! Bool == true {
self.albums.append(Album(name: name, artist: artist,
songs: albumSongs, releaseDate: releaseDate, price: price, albumImageUrl: albumImageUrl))
print("albums: ",albums)
}
}
}
}
}
}
I call my fetchAlbums() in my view .onAppear()
My AlbumSong:
struct AlbumSong: Identifiable, Codable {
#DocumentID var id: String? = UUID().uuidString
let title: String
let duration: TimeInterval
var image: String
let artist: String
let track: String
}
My Album:
struct Album: Identifiable, Codable {
#DocumentID var id: String? = UUID().uuidString
let name: String
let artist: String
let songs: [AlbumSong]
let releaseDate: Date
let price: Int
let albumImageUrl: String
}
I tried looking into how to fetch data from firebase with async function but I couldn't get my code to work and using dispatchGroup worked fine when I only have one album. I would appreciate answers explaining how this code would work with async, I really tried my best figuring it out by myself a long time. Also I would love to know what exactly is happening with DispatchGroup and why it works fine having one album but not with multiple ones.
I think you are over complicating something that is very simple with async await
First, your Models need some adjusting, it may be the source of some of your issues.
import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
struct AlbumSong: Identifiable, Codable {
//No need to set a UUID `#DocumentID` provides an ID
#DocumentID var id: String?
let title: String
let duration: TimeInterval
var image: String
let artist: String
let track: String
}
struct Album: Identifiable, Codable {
//No need to set a UUID `#DocumentID` provides an ID
#DocumentID var id: String?
let name: String
let artist: String
//Change to var and make nil, the initial decoding will be blank
//If any of the other variables might be optional add the question mark
var songs: [AlbumSong]?
let releaseDate: Date
let price: Int
let albumImageUrl: String
}
Then you can create a service that does the heavy lifting with the Firestore.
struct NestedFirestoreService{
private let store : Firestore = .firestore()
let ALBUM_PATH = "albums"
let SONG_PATH = "songs"
///Retrieves Albums and Songs
func retrieveAlbums() async throws -> [Album] {
//Get the albums
var albums: [Album] = try await retrieve(path: ALBUM_PATH)
//Get the songs, **NOTE: leaving the array of songs instead of making a separate collection might work best.
for (idx, album) in albums.enumerated() {
if let id = album.id{
albums[idx].songs = try await retrieve(path: "\(ALBUM_PATH)/\(id)/\(SONG_PATH)")
}else{
print("\(album) :: has invalid id")
}
}
//Add another loop for `unlocked` here just like the one above.
return albums
}
///retrieves all the documents in the collection at the path
public func retrieve<FC : Identifiable & Codable>(path: String) async throws -> [FC]{
let querySnapshot = try await store.collection(path)
.getDocuments()
return try querySnapshot.documents.compactMap { document in
try document.data(as: FC.self)
}
}
}
Then you can implement it with just a few lines in your presentation layer.
import SwiftUI
#MainActor
class AlbumListViewModel: ObservableObject{
#Published var albums: [Album] = []
private let svc = NestedFirestoreService()
func loadAlbums() async throws{
albums = try await svc.retrieveAlbums()
}
}
struct AlbumListView: View {
#StateObject var vm: AlbumListViewModel = .init()
var body: some View {
List(vm.albums, id:\.id) { album in
DisclosureGroup(album.name) {
ForEach(album.songs ?? [], id:\.id){ song in
Text(song.title)
}
}
}.task {
do{
try await vm.loadAlbums()
}catch{
print(error)
}
}
}
}
struct AlbumListView_Previews: PreviewProvider {
static var previews: some View {
AlbumListView()
}
}
If you get any decoding errors make the variables optional by adding the question mark to the type like I did with the array.
Just use them in the correct order:
let group = DispatchGroup()
#State var albums: [Album] = []
#State var albumSongs: [AlbumSong] = []
func fetchAlbums() {
group.enter()
FirebaseManager.shared.firestore.collection("albums").getDocuments { querySnapshot, error in
if let error = error {
print(error.localizedDescription)
group.leave()
return
}
guard let documents = querySnapshot?.documents else {
group.leave()
return
}
let uid = FirebaseManager.shared.auth.currentUser?.uid
documents.forEach { document in
let data = document.data()
let name = data["name"] as? String ?? ""
let artist = data["artist"] as? String ?? ""
let releaseDate = data["releaseDate"] as? Date ?? Date()
let price = data["price"] as? Int ?? 0
let albumImageUrl = data["albumImageUrl"] as? String ?? ""
let docID = document.documentID
group.enter()
FirebaseManager.shared.firestore.collection("albums").document(docID)
.collection("songs").getDocuments { querySnapshot, error in
if let error = error {
group.leave()
return
}
guard let documents = querySnapshot?.documents else {
group.leave()
return
}
self.albumSongs = documents.compactMap { document -> AlbumSong? in
do {
group.leave()
return try document.data(as: AlbumSong.self)
} catch {
group.leave()
return nil
}
}
}
group.enter()
FirebaseManager.shared.firestore.collection("albums").document(docID)
.collection("unlocked").document(uid ?? "").getDocument { docSnapshot, error in
if let error = error {
group.leave()
return
}
guard let document = docSnapshot?.data() else {
group.leave()
return
}
if document["locked"] as! Bool == true {
self.albums.append(Album(name: name, artist: artist,
songs: albumSongs, releaseDate: releaseDate, price: price, albumImageUrl: albumImageUrl))
print("albums: ",albums)
}
group.leave()
}
}
group.leave()
}
group.notify(queue: DispatchQueue.global()) {
// do your stuff
}
}

How do I read a User's Firestore Map to a Swift Dictionary?

I have my user struct with has a dictionary of all their social medias.
struct User: Identifiable {
var id: String { uid }
let uid, email, name, bio, profileImageUrl: String
let numSocials, followers, following: Int
var socials: [String: String]
init(data: [String: Any]) {
self.uid = data["uid"] as? String ?? ""
self.email = data["email"] as? String ?? ""
self.name = data["name"] as? String ?? ""
self.bio = data["bio"] as? String ?? ""
self.profileImageUrl = data["profileImageURL"] as? String ?? ""
self.numSocials = data["numsocials"] as? Int ?? 0
self.followers = data["followers"] as? Int ?? 0
self.following = data["following"] as? Int ?? 0
self.socials = data["socials"] as? [String: String] ?? [:]
}
}
The idea is for socials (the dictionary), to be dynamic, since users can add and remove social medias. Firestore looks like this:
The dictionary is initialized as empty. I have been able to add elements to the dictionary with this function:
private func addToStorage(selectedMedia: String, username: String) -> Bool {
if username == "" {
return false
}
guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {
print("couldnt get uid")
return false
}
FirebaseManager.shared.firestore.collection("users").document(uid).setData([ "socials": [selectedMedia:username] ], merge: true)
print("yoo")
return true
}
However I can't seem to read the firestore map into my swiftui dictionary. I want to do this so that I can do a ForEach loop and list all of them. If the map is empty then the list would be empty too, but I can't figure it out.
Just in case, here is my viewmodel.
class MainViewModel: ObservableObject {
#Published var errorMessage = ""
#Published var user: User?
init() {
DispatchQueue.main.async {
self.isUserCurrentlyLoggedOut = FirebaseManager.shared.auth.currentUser?.uid == nil
}
fetchCurrentUser()
}
func fetchCurrentUser() {
guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {
self.errorMessage = "Could not find firebase uid"
print("FAILED TO FIND UID")
return
}
FirebaseManager.shared.firestore.collection("users").document(uid).getDocument { snapshot, error in
if let error = error {
self.errorMessage = "failed to fetch current user: \(error)"
print("failed to fetch current user: \(error)")
return
}
guard let data = snapshot?.data() else {
print("no data found")
self.errorMessage = "No data found"
return
}
self.user = .init(data: data)
}
}
}
TLDR: I can't figure out how to get my firestore map as a swiftui dictionary. Whenever I try to access my user's dictionary, the following error appears. If I force unwrap it crashes during runtime. I tried to coalesce with "??" but I don't know how to make it be the type it wants.
ForEach(vm.user?.socials.sorted(by: >) ?? [String:String], id: \.key) { key, value in
linkDisplay(social: key, handler: value)
.listRowSeparator(.hidden)
}.onDelete(perform: delete)
error to figure out
Please be patient. I have been looking for answers through SO and elsewhere for a long time. This is all new to me. Thanks in advance.
This is a two part answer; Part 1 addresses the question with a known set of socials (Github, Pinterest, etc). I included that to show how to map a Map to a Codable.
Part 2 is the answer (TL;DR, skip to Part 2) so the social can be mapped to a dictionary for varying socials.
Part 1:
Here's an abbreviated structure that will map the Firestore data to a codable object, including the social map field. It is specific to the 4 social fields listed.
struct SocialsCodable: Codable {
var Github: String
var Pinterest: String
var Soundcloud: String
var TikTok: String
}
struct UserWithMapCodable: Identifiable, Codable {
#DocumentID var id: String?
var socials: SocialsCodable? //socials is a `map` in Firestore
}
and the code to read that data
func readCodableUserWithMap() {
let docRef = self.db.collection("users").document("uid_0")
docRef.getDocument { (document, error) in
if let err = error {
print(err.localizedDescription)
return
}
if let doc = document {
let user = try! doc.data(as: UserWithMapCodable.self)
print(user.socials) //the 4 socials from the SocialsCodable object
}
}
}
Part 2:
This is the answer that treats the socials map field as a dictionary
struct UserWithMapCodable: Identifiable, Codable {
#DocumentID var id: String?
var socials: [String: String]?
}
and then the code to map the Firestore data to the object
func readCodableUserWithMap() {
let docRef = self.db.collection("users").document("uid_0")
docRef.getDocument { (document, error) in
if let err = error {
print(err.localizedDescription)
return
}
if let doc = document {
let user = try! doc.data(as: UserWithMapCodable.self)
if let mappedField = user.socials {
mappedField.forEach { print($0.key, $0.value) }
}
}
}
}
and the output for part 2
TikTok ogotok
Pinterest pintepogo
Github popgit
Soundcloud musssiiiccc
I may also suggest taking the socials out of the user document completely and store it as a separate collection
socials
some_uid
Github: popgit
Pinterest: pintepogo
another_uid
Github: git-er-done
TikTok: dancezone
That's pretty scaleable and allows for some cool queries: which users have TikTok for example.

Best way to use UserDefaults with ObservableObject in Swiftui

I'm trying to save user basic's data in UserDefaults.
My goal is to be able to consume data from UserDefaults and to update them each time the user do some changes.
I'm using an ObservableObject class to set and get these data
class SessionData : ObservableObject {
#Published var loggedInUser: User = User(first_name: "", last_name: "", email: "")
static let shared = SessionData()
func setLoggedInUser (user: User) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(user) {
UserDefaults.standard.set(encoded, forKey: "User")
self.loggedInUser = currentUser
}
}
and also
struct ProfileView: View {
#ObservedObject var sessionData: SessionData = SessionData.shared
var body: some View {
VStack{
Text(self.sessionData.loggedInUser.first_name)
}
}
}
This way the changes are updated. But if I leave the app I will lose the data.
Solution 2:
I also tried to rely on reading the data from UserDefault like this
class SessionData : ObservableObject {
func getLoggedInUser() -> User? {
if let currentUser = UserDefaults.standard.object(forKey: "User") as? Data {
let decoder = JSONDecoder()
if let loadedUser = try? decoder.decode(User.self, from: currentUser) {
return loadedUser
}
}
return nil
}
}
Problem: I don't get the updates once a user change something :/
I don't find a nice solution to use both UserDefaults and ObservableObject
in "getLoggedInUser()" you are not updating the published var "loggedInUser".
Try this to do the update whenever you use the function:
func getLoggedInUser() -> User? {
if let currentUser = UserDefaults.standard.object(forKey: "User") as? Data {
let decoder = JSONDecoder()
if let loadedUser = try? decoder.decode(User.self, from: currentUser) {
loggedInUser = loadedUser // <--- here
return loadedUser
}
}
return nil
}
or just simply this:
func getLoggedInUser2() {
if let currentUser = UserDefaults.standard.object(forKey: "User") as? Data {
let decoder = JSONDecoder()
if let loadedUser = try? decoder.decode(User.self, from: currentUser) {
loggedInUser = loadedUser // <--- here
}
}
}
You could also do this to automatically save your User when it changes (instead of using setLoggedInUser):
#Published var loggedInUser: User = User(first_name: "", last_name: "", email: "") {
didSet {
if let encoded = try? JSONEncoder().encode(loggedInUser) {
UserDefaults.standard.set(encoded, forKey: "User")
}
}
}
and use this as init(), so you get back what you saved when you leave the app:
init() {
getLoggedInUser2()
}

Changing from Firebase to Firestore caused Fatal error: Unexpectedly found nil ... in a query

I am trying to retrieve a user's data from firestore, after migrating my data there from firebase. The code below was working fine with firebase, and retrieved the user's data.
However, after changing the query to firestore query, I got this error.
Print statement here "document.data()" contains the data, But I got this error. I don't know where this error is coming from.
When I compare document.data() with nil, I got "Document data: contains nil".
I don't know how I suppoused to get the data.
here is the code where I get the error,
static func getUser(uid: String, setUserDefaults: #escaping (NormalUser) -> Void){
DataService.ds.REF_USERS_NORMAL.document(uid).getDocument { (document, error) in
if error != nil{
print("\(String(describing: error?.localizedDescription))")
}else{
if document != nil{
let data = document?.data() as! [String: String]
print("Document data: \(String(describing: document?.data() as! [String: String]))")
let user = NormalUser(userData: (data as Dictionary<String, AnyObject>))
setUserDefaults(user)
}else{
print("Document data: contains nil")
}
}
}
}
Here is how I defined the variables,
import Foundation
import Firebase
class NormalUser: User {
private var _email: String?
private var _city: String?
private var _country: String?
private var _name: String?
private var _phone: String?
private var _profileImgUrl: String?
var email: String {
return _email!
}
var city: String {
return _city!
}
var country: String {
return _country!
}
var name: String {
return _name!
}
var phone: String {
return _phone!
}
var profileImgUrl: String {
set{
self.profileImgUrl = _profileImgUrl!
}
get{
if let pI = _profileImgUrl{
return pI
}
return ""
}
}
init(userData: Dictionary<String, AnyObject>) {
super.init(userId: userData["uid"] as! String, user: userData)
if let email = userData["email"] as? String {
self._email = email
}
if let city = userData["city"] as? String {
self._city = city
}
if let country = userData["country"] as? String {
self._country = country
}
if let name = userData["name"] as? String {
self._name = name
}
if let phone = userData["phone"] as? String {
self._phone = phone
}
if let profileImgUrl = userData["imgUrl"] as? String {
self._profileImgUrl = profileImgUrl
}
}
static func createNormalUser(uid: String, userData: Dictionary<String, String>) {
// add user to database
//DataService.ds.REF_USERS_NORMAL.child(uid).setValue(userData)
DataService.ds.REF_USERS_NORMAL.document(uid).setData(userData) { (err) in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
addUserToGroup(uid:uid, group:"normal")
}
static func updateUserProfile(uid: String, userData: Dictionary<String, String>) {
//DataService.ds.REF_USERS_NORMAL.child(uid).updateChildValues(userData)
DataService.ds.REF_USERS_NORMAL.document(uid).updateData(userData)
}
static func getUser(uid: String, setUserDefaults: #escaping (NormalUser) -> Void){
DataService.ds.REF_USERS_NORMAL.document(uid).getDocument { (document, error) in
if error != nil{
print("\(String(describing: error?.localizedDescription))")
}else{
if document != nil{
let data = document?.data() as! [String: String]
print("Document data: \(String(describing: document?.data() as! [String: String]))")
let user = NormalUser(userData: (data as Dictionary<String, AnyObject>))
setUserDefaults(user)
}else{
print("Document data: contains nil")
}
}
}
}
}
I solved the issue!
For any one who may come across this error, the problem was with userId. I was getting the user id from firebase, which, in my case, no longer serves my query, and eventually getting a nil. When I got the ID directly from authentication,
let userID = Auth.auth().currentUser?.uid
It SOLVED the issue!

Swift - Why is my JSON object element only adding the last array element?

I have a problem with my JSON object. Everything is working fine creating and printing out my JSON object, apart from the idQty part. It only prints the last key value result. I assume I have a problem with my for loop. If anybody can point out where I've went wrong, it would be of huge help.
Code below:
struct Order: Codable {
let idQty: [IdQty]
let collection: String
let name: String
let phone: Int
let doorNum: Int
let street: String
let postcode: String
}
struct IdQty: Codable {
let itemId: Int
let qty: Int
}
class CheckoutServer: NSObject, URLSessionDataDelegate {
var inputVals = [Int:Int]()
var idQty = [IdQty]()
var collection = String()
var name = String()
var phone = Int()
var doorNum = Int()
var street = String()
var postcode = String()
var request = URLRequest(url: NSURL(string: "http://192.168.1.100/api/AddOrder.php")! as URL)
func downloadItems() {
for(key,value) in inputVals {
idQty = [IdQty(itemId: key,qty: value)]
}
let order = Order(idQty: idQty,collection: collection,name: name,phone: phone,doorNum: doorNum,street: street,postcode: postcode)
let encodedOrder = try? JSONEncoder().encode(order)
var json: Any?
request.httpMethod = "POST"
if let data = encodedOrder {
json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let json = json {
}
}
let postParameters = "json="+String(describing: json!)
request.httpBody = postParameters.data(using: .utf8)
print(String(describing: json!))
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Failed to upload data at Menu Type Items")
} else {
print("Data uploaded")
}
}
task.resume()
}
}
Below is the output. the 'idQty' part only ever returns the last entry in the [Int:Int] dictionary:
{
collection = Delivery;
doorNum = 4;
idQty = (
{
itemId = 14;
qty = 2;
}
);
name = James;
phone = 4355345;
postcode = Test;
street = TestStreet;
}
You should append new value to your array instead of recreating it on each iteration
for(key,value) in inputVals
{
idQty.append(IdQty(itemId: key,qty: value))
}