I try to connect Realm to my recipe application in Swift. I have a problem about the ingredients : when I run my app, I have this error coming
There must be a primary key property named '_id' on a synchronized Realm but none was found for type 'recipe_ingredients'
I assume it is because I typed my ingredient object as a Realm Object and it wants a primary key. I tried to add one, but then it says
Primary Key for class 'recipe_ingredients' has been added.
Here is my model file :
import RealmSwift
import Foundation
class recipe: Object {
#Persisted(primaryKey: true) var _id: ObjectId?
#Persisted var name: String?
#Persisted var category: String?
#Persisted var image: String?
#Persisted var note: Double?
#Persisted var likes: Int?
#Persisted var ingredients: List<recipe_ingredients>
#Persisted var steps: List<recipe_steps>
#Persisted var time: String?
#Persisted var units: Int?
convenience init(name: String) {
self.init()
self.name = name
}
}
class recipe_ingredients: Object{
#Persisted(primaryKey: true) var _id: ObjectId?
#Persisted var name: String?
#Persisted var quantity: String?
}
class recipe_steps: Object{
#Persisted(primaryKey: true) var _id: ObjectId?
#Persisted var step_description: String?
#Persisted var step: Int?
}
And here is the Realm Schema
{
"title": "recipe",
"properties": {
"__v": {
"bsonType": "int"
},
"_id": {
"bsonType": "objectId"
},
"category": {
"bsonType": "string"
},
"image": {
"bsonType": "string"
},
"ingredients": {
"bsonType": "array",
"items": {
"bsonType": "object",
"properties": {
"name": {
"bsonType": "string"
},
"quantity": {
"bsonType": "string"
}
}
}
},
"likes": {
"bsonType": "int"
},
"name": {
"bsonType": "string"
},
"note": {
"bsonType": "double"
},
"steps": {
"bsonType": "array",
"items": {
"bsonType": "object",
"properties": {
"description": {
"bsonType": "string"
},
"step": {
"bsonType": "int"
}
}
}
},
"time": {
"bsonType": "string"
},
"units": {
"bsonType": "int"
}
}
}
Thanks for your help !
Related
I am trying to consume data from an api in swift, json data has successfully been delivered back to the app but the json response from my backend is very complex hence forming struct for my model is very difficult. I'm able to only retrieve the simple strings but if I add the objects and arrays everything stops working
[
{
"type": "movie",
"id": "ffff-ddd968-4cf0-a939-8skeu",
"title": "Sorority Row",
"description": "When five laldkdk",
"categories": [],
"genres": [
"Mystery",
"Horror"
],
"links": {
"amazonPrime": [
{
"link": "somelink",
"format": "native_ios"
},
{
"link": "somelink",
"format": "native_ios"
}
],
"appleTvPlus": [],
"disneyPlus": []
"iTunes": [
{
"link": "www.somelink",
"format": "webview_computer"
}
],
"netflix": [],
"youTubePremium": []
},
"promoted": false,
"certification": "18",
"releaseDate": "2009-09-09",
"runTime": 101,
"userRating": null,
"inUserList": false,
"packShot": {
"thumbnail": "imageurl"
},
"backdrop": {
"thumbnail": "imageurl"
}
}
]
struct Responder: Codable {
let type: String
let id: String
let description: String
let title: String
let promoted: Bool
let certification: String
let firstAirDate: String
let lastAirDate: String
let numberEpisodes: Int
let numberSeasons: Int
let userRating: Int?
let inUserList: Bool
let thumbnail: PackShotObj
let amazonPrime: linksObj
}
struct PackShotObj: Codable {
let packShot: [String]
}
struct linksObj: Codable {
let link: String
let format: String
}
struct genres: Codable {
let empty: String
}
Here is the code that works, decoding your json data. Note the differences between my struct models and yours. You will need to consult the docs of the server to determine which fields are optionals and adjust the code for that:
struct ContentView: View {
#State var responders: [Responder] = []
var body: some View {
List(responders) { responder in
Text(responder.title)
Text(responder.description)
Text(responder.releaseDate)
}
.onAppear {
let json = """
[
{
"type": "movie",
"id": "ffff-ddd968-4cf0-a939-8skeu",
"title": "Sorority Row",
"description": "When five laldkdk",
"categories": [],
"genres": [
"Mystery",
"Horror"
],
"links": {
"amazonPrime": [
{
"link": "somelink",
"format": "native_ios"
},
{
"link": "somelink",
"format": "native_ios"
}
],
"appleTvPlus": [],
"disneyPlus": [],
"iTunes": [
{
"link": "www.somelink",
"format": "webview_computer"
}
],
"netflix": [],
"youTubePremium": []
},
"promoted": false,
"certification": "18",
"releaseDate": "2009-09-09",
"runTime": 101,
"userRating": null,
"inUserList": false,
"packShot": {
"thumbnail": "imageurl"
},
"backdrop": {
"thumbnail": "imageurl"
}
}
]
"""
// simulated API data
let data = json.data(using: .utf8)!
do {
self.responders = try JSONDecoder().decode([Responder].self, from: data)
print("\n---> responders: \n \(responders)\n")
} catch {
print("\n---> decoding error: \n \(error)\n")
}
}
}
}
// MARK: - Responder
struct Responder: Identifiable, Codable {
let type, id, title, description: String
let categories: [String]
let genres: [String]
let links: Links
let promoted: Bool
let certification, releaseDate: String
let runTime: Int
let userRating: Int?
let inUserList: Bool
let packShot, backdrop: Backdrop
}
// MARK: - Backdrop
struct Backdrop: Codable {
let thumbnail: String
}
// MARK: - Links
struct Links: Codable {
let amazonPrime: [Provider]
let appleTvPlus: [Provider]
let disneyPlus: [Provider]
let iTunes: [Provider]
let netflix: [Provider]
let youTubePremium: [Provider]
}
struct Provider: Codable {
let link, format: String
}
Just copy and paste this model to file and you are good to go.
struct Responder: Codable {
let type, id, title, welcomeDescription: String
let categories: [String]
let genres: [String]
let links: Links
let promoted: Bool
let certification, releaseDate: String
let runTime: Int
let userRating: Any?
let inUserList: Bool
let packShot, backdrop: Backdrop
enum CodingKeys: String, CodingKey {
case type, id, title
case welcomeDescription = "description"
case categories, genres, links, promoted, certification, releaseDate, runTime, userRating, inUserList, packShot, backdrop
}
}
// MARK: - Backdrop
struct Backdrop: Codable {
let thumbnail: String
}
// MARK: - Links
struct Links: Codable {
let amazonPrime: [AmazonPrime]
let appleTvPlus, disneyPlus: [String]
let iTunes: [AmazonPrime]
let netflix, youTubePremium: [String]
}
// MARK: - AmazonPrime
struct AmazonPrime: Codable {
let link, format: String
}
I've a response from MapBox API. I want to store each Feature object of features array in a array then want to display from that array to table view.
{
type: "FeatureCollection",
query: [
"u"
],
features: [{
id: "country.19678805456372290",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q30",
short_code: "us"
},
text: "United States",
place_name: "United States",
matching_text: "US",
matching_place_name: "US",
bbox: [
-179.9,
18.8163608007951,
-66.8847646185949,
71.4202919997506
],
center: [
-97.9222112121185,
39.3812661305678
],
geometry: {
type: "Point",
coordinates: [
-97.9222112121185,
39.3812661305678
]
}
},
{
id: "country.12405201072814600",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q145",
short_code: "gb"
},
text: "United Kingdom",
place_name: "United Kingdom",
bbox: [
-8.74974065661991,
49.802416901086,
1.86276379960989,
60.9093517989553
],
center: [
-2.36966957036279,
54.2379333607472
],
geometry: {
type: "Point",
coordinates: [
-2.36966957036279,
54.2379333607472
]
}
},
{
id: "country.11702916565595680",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q878",
short_code: "ae"
},
text: "United Arab Emirates",
place_name: "United Arab Emirates",
bbox: [
51.4160146192147,
22.6282410017159,
56.4814183948386,
26.094609499407
],
center: [
54.2561723713588,
23.8520599823879
],
geometry: {
type: "Point",
coordinates: [
54.2561723713588,
23.8520599823879
]
}
},
{
id: "country.11871993712476590",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q212",
short_code: "ua"
},
text: "Ukraine",
place_name: "Ukraine",
bbox: [
22.1375690033684,
44.3152913001972,
40.2255909904191,
52.3793529890401
],
center: [
31.3202829593814,
49.3227937844972
],
geometry: {
type: "Point",
coordinates: [
31.3202829593814,
49.3227937844972
]
}
},
{
id: "country.9140805109496660",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q77",
short_code: "uy"
},
text: "Uruguay",
place_name: "Uruguay",
matching_text: "URY",
matching_place_name: "URY",
bbox: [
-58.4891219951353,
-35.0552819973662,
-53.1014000735687,
-30.0855740544354
],
center: [
-56.012396238658,
-32.7996455126793
],
geometry: {
type: "Point",
coordinates: [
-56.012396238658,
-32.7996455126793
]
}
}
],
attribution: "NOTICE: © 2021 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare."
}
So, I create a model like this -
struct Response: Codable {
var features: [Feature]
}
struct Feature: Codable {
var id: String!
var type: String?
var matching_place_name: String?
var place_name: String?
var geometry: Geometry
var center: [Double]
var properties: Properties
}
struct Geometry: Codable {
var type: String?
var coordinates: [Double]
}
struct Properties: Codable {
var address: String?
}
And Write this code -
var suggestedPlacenames: NSMutableArray = []
func doShowSuggestion(usersQuery: String) {
let urlString = "\(mapbox_api)\(usersQuery).json?access_token=\(mapbox_access_token)"
let url = URL(string: urlString)
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard let data = data, error == nil else {
print("Error in URLSeesion")
return
}
if let result = try? JSONDecoder().decode(Response.self, from: data) {
self.suggestedPlacenames.add(result)
print(self.suggestedPlacenames.count)
} else {}
But by this I always get 1 length from self.suggestedPlacenames whatever the length of the response in features. How can I get each features object separately in self.suggestedPlacenames?
In your code :
self.suggestedPlacenames.add(result)
You add only one element to your array.
You must append the an array.
// declare as an array of features
var suggestedPlacenames = [Feature]()
self.suggestedPlacenames.append(contentsOf: result.features)
or
self.suggestedPlacenames += result.features
You need to delcare
var allFeatures = [Feature]()
do {
let result = try JSONDecoder().decode(Response.self, from: data)
self.allFeatures = result.features
print(self.allFeatures.count)
}
catch {
print(error)
}
Then
Here i provide the code worked sample as per the guideline given below and seems to get null values.
Here is my complete JSON Data,
some: {
"success": true,
"data":
[
{
"15-10-2020": [
{
"id": 100,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 101,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 102,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
}
],
"30-09-2020": [
{
"snf_id": 301,
"details": {
"_id": 8,
"_title": "My Title"
},
"created_at": "2020-09-30"
}
]
}
],
"message": "Successfully Retrieved"
}
struct Response : Codable {
var success : Bool?
var data : [Data]?
var message : String?
}
struct Data: Codable {
var snf_id: Int?
var details: Details?
var created_at: String?
}
// MARK: - Details
struct Details: Codable {
var _id: Int?
var _title: String?
}
let Response = try JSONDecoder().decode(Response.self, from: data)
Returns null value for data,
▿ Response
▿ success : Optional
- some : true
▿ data : Optional<Array>
▿ some : 1 element
▿ 0 : Data
- snf_id : nil
- details : nil
- created_at : nil
▿ message : Optional
- some : "Successfully Retrieved"
There is no myData key in your json , your json top structure is an array that contains elements where every element value is an array like [[String: [Root]]]
struct Root: Codable {
let id: Int?
let details: Details
let createdAt: String
let snfID: Int?
enum CodingKeys: String, CodingKey {
case id, details
case createdAt = "created_at"
case snfID = "snf_id"
}
}
// MARK: - Details
struct Details: Codable {
let id: Int
let title: String
enum CodingKeys: String, CodingKey {
case id = "_id"
case title = "_title"
}
}
And to decode
let res = try JSONDecoder().decode([[String: [Root]]].self,from:data)
some: {
"success": true,
"data":
[
{
"15-10-2020": [
{
"id": 100,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 101,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 102,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
}
],
"30-09-2020": [
{
"snf_id": 301,
"details": {
"_id": 8,
"_title": "My Title"
},
"created_at": "2020-09-30"
}
]
}
],
"message": "Successfully Retrieved"
}
struct Response : Codable {
var success : Bool?
**var data : [[String:[Data]]]?**
var message : String?
}
struct Data: Codable {
var snf_id: Int?
var details: Details?
var created_at: String?
}
// MARK: - Details
struct Details: Codable {
var _id: Int?
var _title: String?
}
let Response = try JSONDecoder().decode(Response.self, from: data)
I'm using Alamofire and Decodable for a Google Books API request for performing a search.
I've created a Decodable model which I will add below and it worked up until I try to get any other field below "title" in "volumeInfo" (I've been commenting out properties one by one to check where it fails). The error I get after uncommenting "authors", "publisher" or anything other than "title" inside "volumeInfo" is:
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "publisher", intValue: nil)
An example response is:
{
"kind": "books#volume",
"id": "Ett09eLWE5oC",
"etag": "WIwTdsmpnhs",
"selfLink": "https://www.googleapis.com/books/v1/volumes/Ett09eLWE5oC",
"volumeInfo": {
"title": "The Picture of Dorian Gray",
"authors": [
"Oscar Wilde"
],
"publisher": "Wordsworth Editions",
"publishedDate": "1992",
"description": "The handsome appearance of dissolute young Dorian Gray remains unchanged while the features in his portrait become distorted as his degeneration progresses",
"industryIdentifiers": [
{
"type": "ISBN_10",
"identifier": "1853260150"
},
{
"type": "ISBN_13",
"identifier": "9781853260155"
}
],
"readingModes": {
"text": false,
"image": true
},
"pageCount": 312,
"printType": "BOOK",
"categories": [
"Fiction"
],
"averageRating": 4.0,
"ratingsCount": 6,
"maturityRating": "NOT_MATURE",
"allowAnonLogging": false,
"contentVersion": "1.1.2.0.preview.1",
"panelizationSummary": {
"containsEpubBubbles": false,
"containsImageBubbles": false
},
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=Ett09eLWE5oC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=Ett09eLWE5oC&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
},
"language": "en",
"previewLink": "http://books.google.ro/books?id=Ett09eLWE5oC&pg=PA130&dq=9781853260155&hl=&cd=1&source=gbs_api",
"infoLink": "http://books.google.ro/books?id=Ett09eLWE5oC&dq=9781853260155&hl=&source=gbs_api",
"canonicalVolumeLink": "https://books.google.com/books/about/The_Picture_of_Dorian_Gray.html?hl=&id=Ett09eLWE5oC"
}
}
My model:
struct Response: Decodable {
struct VolumeInfo: Decodable {
var title: String
//var authors: [String]
var publisher: String
//var identifiers: [Identifier]
//var image: ImageLink
enum CodingKeys: String, CodingKey {
case title
//case authors
case publisher
//case identifiers = "industryIdentifiers"
//case image = "imageLinks"
}
}
struct Identifier: Decodable {
var type: String
var identifier: String
enum CodingKeys: String, CodingKey {
case type
case identifier
}
}
struct ImageLink: Decodable {
var url: URL
enum CodingKeys: String, CodingKey {
case url = "thumbnail"
}
}
var id: String
var volumeInfo: VolumeInfo
}
i am getting data from an api like this
[
{
"internData": {
"id": "abc123",
"name": "Doctor"
},
"author": "Will smith",
"description": "Is an actor",
"url": "https://www",
},
{
"internData": {
"id": "qwe900",
"name": "Constructor"
},
"author": "Edd Bett",
"description": "Is an Constructor",
"url": "https://www3",
}
]
I have my model like this
struct PersonData: Codable {
let author: String?
let description: String?
let url: String?
}
But I dont know how to define the "internData", I tried with another Model "InterData" and define id and name like the PersonData, but i get an error, i tried also with [String:Any] but i get an error for the Codable protocol
I am using
let resP = try JSONSerialization.jsonObject(with: data, options: .init()) as? [String: AnyObject]
print("resP", )
in my script of Service/Network
Thanks if somebody knows
You can't use [String:Any] type in case of Codable. you need to create an another model of InternData, which is used by PersonData.
Code:
JSON Data :
let jsonData =
"""
[
{
"internData": {
"id": "abc123",
"name": "Doctor"
},
"author": "Will smith",
"description": "Is an actor",
"url": "https://www",
},
{
"internData": {
"id": "qwe900",
"name": "Constructor"
},
"author": "Edd Bett",
"description": "Is an Constructor",
"url": "https://www3",
}
]
"""
// Models
struct PersonData: Codable {
let author: String?
let description: String?
let url: String?
let internData : InternData?
}
// New model
struct InternData : Codable {
let id : String?
let name : String?
}
// Parsing
do {
let parseRes = try JSONDecoder().decode([PersonData].self, from: Data(jsonData.utf8))
print(parseRes)
}
catch {
print(error)
}