I have two tables Category and Sub Category.
Category has toMany Relationship with SubCategory.
SubCategory has toMany Relationship with SubCategory. (SubCategory may have subcategories)
DB Model:
Json Response
{
"response": {
"status": true,
"message": "Category listed",
"code": 200
},
"data": [
{
"description": "Memory Questions",
"id": "QT05",
"title": "Memory",
"subcategory": [
{
"title": "Patterns",
"id": "QT05P",
"subcategory": [
{
"title": "Squares",
"id": "QT05PS",
"demo": {
"type": "image",
"value": "Memory/images/demo_QT05PS.png"
}
},
{
"title": "Circles",
"id": "QT05PC",
"demo": {
"type": "image",
"value": "Memory/images/demo_QT05PC.png"
}
},
{
"title": "Triangles",
"id": "QT05PT",
"demo": {
"type": "image",
"value": "Memory/images/demo_QT05PT.png"
}
},
{
"title": "Rectangles",
"id": "QT05PR",
"demo": {
"type": "image",
"value": "Memory/images/demo_QT05PR.png"
}
}
],
"demo": {
"type": "image",
"value": "Memory/images/demo_QT05P.png"
}
}
]
}
]
}
Sub Category Model Class (CoreData)
extension SubCategory{
func addSubCategory(subcategory:SubCategory) {
var sub = self.mutableSetValueForKey("subcategories")
sub.addObject(subcategory)
}
func setSubCategories(subCategories:NSArray){
var sub = self.mutableSetValueForKey("subcategories")
for subcategory in subCategories {
sub.addObject(subcategory)
}
}
}
Mapping JSON
func mapCategory(dict:NSDictionary?) -> LUBaseResponse? {
if (dict == nil) {
return nil
}
var response = self.mapStatus(dict!)
if (response != nil){
if (response!.status == 1){
let data:NSArray = dict!["data"] as NSArray
var categoryArray = NSMutableArray()
for categoryDict in data {
if (self.fetchCategory(categoryDict["id"] as? String) == nil){
var category = NSEntityDescription.insertNewObjectForEntityForName("Category", inManagedObjectContext: self.managedObjectContext!) as? Category
category?.categoryDescription = categoryDict["description"] as String
category?.categoryId = categoryDict["id"] as String
category?.categoryTitle = categoryDict["title"] as String
var subcategory = self.mapSubcategory(categoryDict["subcategory"] as? NSArray,category: category!)
if (subcategory != nil && subcategory?.count > 0){
category?.setSubCategories(subcategory!)
}
categoryArray.addObject(category!)
}
LUCoreData.sharedInstance.saveContext()
}
response?.data = categoryArray
}
}
return response
}
//Issue in this method
private func mapSubcategory(array:NSArray?,category:Category) -> NSMutableArray? {
var subcategoriesObjects = NSMutableArray()
if (array?.count > 0){
var subcategories = array!
NSEntityDescription.insertNewObjectForEntityForName("SubCategory", inManagedObjectContext: self.managedObjectContext!) as? SubCategory
for subcategoryDict in subcategories {
var title = subcategoryDict["title"]
if (self.fetchSubCategory(subcategoryDict["id"] as? String) == nil) {
var subcategory = NSEntityDescription.insertNewObjectForEntityForName("SubCategory", inManagedObjectContext: self.managedObjectContext!) as? SubCategory
subcategory?.title = subcategoryDict["title"] as String
subcategory?.id = subcategoryDict["id"] as String
subcategory?.categoryOf = category
var arraySubCategory = subcategoryDict.objectForKey("subcategory") as? NSArray
if (arraySubCategory?.count > 0){
var nestedSubCategory = self.mapSubcategory(arraySubCategory,category: category)
if (nestedSubCategory != nil){
var sub = nestedSubCategory!.objectAtIndex(0) as SubCategory
println("Sub is \(sub.subcategories)") //Returns Empty
subcategory!.setSubCategories(nestedSubCategory!)
//Issue Here
println("Sub is \(sub.subcategories)")
}
}
subcategoriesObjects.addObject(subcategory!)
}
LUCoreData.sharedInstance.saveContext()
}
}
return subcategoriesObjects
}
Problem:
In JSON Response, you can see that Memory(Category) has subcategories "Patterns", which has nested SubCategories "Square","Circles" etc.
This looping is executed in "mapSubcategory" function
After this line "subcategory!.setSubCategories(nestedSubCategory!)"
In the above images, you can see that when nested subcategory("Squares" which has no further subcategories) added to parent subcategory, the parent subcategory(patterns) is added to Square as subcategory. This cause recursive (Parent -> Child -> Parent -> Child ....). I am not sure how it is recursively adding parent to child.
I have fixed this by removing inverse relationship from subcategory table.
Related
I don't want FetchRequest to return [QuestionCD] array. Can't FetchRequest return QuestionCD to me?
each test has a unique title. There are 50 questions in total in a test. The title here determines the category of the questions.
With this function, I save the questions of that test according to the title of the selected test.
Is my Core Data model wrong? I want to categorize each test by title. I want questions to be returned to me according to the searched title. For example: "title: August Test 2". Please review my json construction and coreData models.
Save Category Function:
func saveSelectedCategory(title: String) {
let allCategory = QuestionCD(context: persistentContainer.viewContext)
allCategory.title = title
do {
try persistentContainer.viewContext.save()
} catch {
print("Failed to save selected category: \(error.localizedDescription)")
}
}
After saving the category, I transfer the questions in the form of an array to the questions parameter in the same model and save it.
Save Question Function:
[QuestionList] is my custom model. I'm saving the questions I get from json to CoreData using this model.
func saveSelectedQuestion(questions: [QuestionList]) {
let question = QuestionCD(context: persistentContainer.viewContext)
question.questions = questions
do {
try persistentContainer.viewContext.save()
} catch {
print("Failed to save selected category: \(error.localizedDescription)")
}
}
Get Selected Question:
Here I am looking for the category of the selected title in QuestionCD according to the selected title.
You can review the json construction. eg title: "Ağustos Test 2"
I don't want the return result to be "[QuestionCD]". I want the returned result to be QuestionCD. Is this possible ?
You can examine the QuestionCD model from the Core Data Entites image.
If QuestionCD is not an array, I can run it in a single ForEach to get the questions.
func getSelectedQuestion(questionID: String) -> [QuestionCD] {
let fetchRequest: NSFetchRequest<QuestionCD> = QuestionCD.fetchRequest()
let search = NSPredicate(format: "title CONTAINS %#", questionID)
print("search: \(search)")
fetchRequest.predicate = search
do {
return try persistentContainer.viewContext.fetch(fetchRequest)
} catch {
return []
}
}
Core Data Entities:
JSON:
{
"allQuiz": [
{
"title":"Ağustos Test 1",
"questions": [
{
"id": "1",
"question":"Şekle göre aşağıdakiler hangisi doğrudur",
"isQuestionImage": true,
"isSectionImage": false,
"imageURL":"https://firebasestorage.googleapis.com/v0/b/ehliyet-sinavim-01.appspot.com/o/Agustos%20Test%201%2F1.png?alt=media&token=2881447c-9081-4b13-a7ad-3ad097886b04",
"sections": {
"A":"2 numaralı aracın öncelikle geçmesi",
"B":"1 numaralı aracın hızını arttırarak kavşağa girmesi",
"C":"2 numaralı aracın 3 numaralı aracın geçmesini beklemesi",
"D":"3 numaralı aracın 2 numaralı aracı ikaz ederek durdurması"
},
"selected":"",
"correct": "A"
},
{
"id": "2",
"question":"Akaryakıt istasyonundan yola çıkmak isteyen şekildeki 2 numaralı araç sürücüsü ne yapmalıdır ?",
"isQuestionImage": true,
"isSectionImage": false,
"imageURL":"https://firebasestorage.googleapis.com/v0/b/ehliyet-sinavim-01.appspot.com/o/Agustos%20Test%201%2F2.jpg?alt=media&token=94b833ff-3462-445a-9054-94b3cdaaa668",
"sections": {
"A":"Selektör yaparak 1 numarlı aracı durdurmalıdır.",
"B":"Korna çalıp 1 numralı aracı yavaşlatmalıdır.",
"C":"1 numaralı aracın geçmesini beklemelidir.",
"D":"Geçiş hakkını kendi kullanmalıdır."
},
"selected":"",
"correct": "C"
},
...........
]
},
{
"title":"Ağustos Test 2",
"questions": [
{
"id": "1",
"question":"Şekle göre aşağıdakiler hangisi doğrudur",
"isQuestionImage": true,
"isSectionImage": false,
"imageURL":"https://firebasestorage.googleapis.com/v0/b/ehliyet-sinavim-01.appspot.com/o/Agustos%20Test%201%2F1.png?alt=media&token=*****-a7ad-3ad097886b04",
"sections": {
"A":"2 numaralı aracın öncelikle geçmesi",
"B":"1 numaralı aracın hızını arttırarak kavşağa girmesi",
"C":"2 numaralı aracın 3 numaralı aracın geçmesini beklemesi",
"D":"3 numaralı aracın 2 numaralı aracı ikaz ederek durdurması"
},
"selected":"",
"correct": "A"
},
{
"id": "2",
"question":"Akaryakıt istasyonundan yola çıkmak isteyen şekildeki 2 numaralı araç sürücüsü ne yapmalıdır ?",
"isQuestionImage": true,
"isSectionImage": false,
"imageURL":"https://firebasestorage.googleapis.com/v0/b/ehliyet-sinavim-01.appspot.com/o/Agustos%20Test%201%2F2.jpg?alt=media&token=*****-9054-94b3cdaaa668",
"sections": {
"A":"Selektör yaparak 1 numarlı aracı durdurmalıdır.",
"B":"Korna çalıp 1 numralı aracı yavaşlatmalıdır.",
"C":"1 numaralı aracın geçmesini beklemelidir.",
"D":"Geçiş hakkını kendi kullanmalıdır."
},
"selected":"",
"correct": "C"
},
...........
]
}
]
}
Model:
class QuestionContainer: NSObject, Codable{
var questions: Question
init(questions: Question) {
self.questions = questions
}
}
class Question: NSObject, Codable {
var title: String
var questions: [QuestionList]
init(title: String, questions: [QuestionList]) {
self.title = title
self.questions = questions
}
}
public class QuestionList: NSObject, Codable {
var id: String
var question: String
var isQuestionImage, isSectionImage: Bool
var imageURL: String
var imageData: Data?
var sections: [QuestionSections.RawValue : String]
var selected: String
var correct: String
init(id: String, question: String, isQuestionImage: Bool, isSectionImage: Bool, imageURL: String, sections: [QuestionSections.RawValue : String], selected: String, correct: String) {
self.id = id
self.question = question
self.isQuestionImage = isQuestionImage
self.isSectionImage = isSectionImage
self.imageURL = imageURL
self.sections = sections
self.selected = selected
self.correct = correct
}
}
CoreData Manager:
class CoreDataManager: ObservableObject {
let persistentContainer: NSPersistentContainer
init() {
persistentContainer = NSPersistentContainer(name: "EhliyetSinavim")
persistentContainer.loadPersistentStores { description, error in
if let error = error {
fatalError("Core Data Stre failed: \(error.localizedDescription)")
}
}
}
func saveSelectedCategory(title: String) {
let allCategory = QuestionCD(context: persistentContainer.viewContext)
allCategory.title = title
do {
try persistentContainer.viewContext.save()
} catch {
print("Failed to save selected category: \(error.localizedDescription)")
}
}
func getSelectedCategory() -> [QuestionCD] {
let fetchRequest: NSFetchRequest<QuestionCD> = QuestionCD.fetchRequest()
let sort = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [sort]
do {
return try persistentContainer.viewContext.fetch(fetchRequest)
} catch {
return []
}
}
func searchInCategory(text: String) -> [QuestionCD] {
let fetchRequest: NSFetchRequest<QuestionCD> = QuestionCD.fetchRequest()
let search = NSPredicate(format: "ANY title == %#", text)
print("search: \(search)")
fetchRequest.predicate = search
print("request predicate: \(String(describing: fetchRequest.predicate))")
do {
return try persistentContainer.viewContext.fetch(fetchRequest)
} catch {
print("ver bulunamadı \n")
return []
}
}
//MARK: ForQuestionCategory
func saveSelectedQuestion(title: String, questions: [QuestionList]) {
let question = QuestionCD(context: persistentContainer.viewContext)
question.title = title
question.questions = questions
do {
try persistentContainer.viewContext.save()
} catch {
print("Failed to save selected category: \(error.localizedDescription)")
}
}
func getSelectedQuestion(questionID: String) -> [QuestionCD] {
let fetchRequest: NSFetchRequest<QuestionCD> = QuestionCD.fetchRequest()
let search = NSPredicate(format: "title CONTAINS %#", questionID)
print("search: \(search)")
fetchRequest.predicate = search
do {
return try persistentContainer.viewContext.fetch(fetchRequest)
} catch {
return []
}
}
}
You could return the first object from the array of fetched objects:
func getSelectedQuestion(questionID: String) -> QuestionCD? {
let fetchRequest: NSFetchRequest<QuestionCD> = QuestionCD.fetchRequest()
let search = NSPredicate(format: "title CONTAINS %#", questionID)
print("search: \(search)")
fetchRequest.predicate = search
do {
return try persistentContainer.viewContext.fetch(fetchRequest).first
} catch {
return nil
}
}
Want to convert my json object into an array of just names so I can use the search bar to search for different names and filter it.
"data2": {
"id": 1,
"contacts": [
{
"name": "Molly",
"pictureUrl": "molly"
},
{
"name": "Cathy",
"pictureUrl": "molly"
},
{
"name": "Bob",
"pictureUrl": "bob"
},
{
"name": "Nick",
"pictureUrl": "clothes"
}
],
},
"error": 0,
"message": "Success"
This is the json file with the object:
var contact = [IndividualContact]()
init?(data2: Data) {
do {
if let json2 = try JSONSerialization.jsonObject(with: data2) as? [String: Any], let body = json2["data2"] as? [String: Any] {
if let contacts = body["contacts"] as? [[String: Any]] {
self.contact = ( contacts.map { IndividualContact(json2: $0) } )
//Expected: ["Molly", "Bob", "Cathy"]
}
}
I have this json file:
[
{
"person": {
"#id": "value1",
"name": "Mattia"
},
"person1": {
"#ref": "value1"
},
"subPersons": [
{
"#id": "value2",
"name": "Luca",
"key": {
"#ref": "value1"
}
},
{
"#ref": "value1"
},
{
"#id": "value3",
"subsubPersons": [
{
"again": {
"#ref": "value2"
}
}
]
}
],
"key": {
"subKey": {
"#ref": "value1"
}
}
}
]
I need to map all objects that contains a #id so replace all #ref values with related #id values mapped. I'd like to obtain this:
[
{
"person": {
"#id": "value1",
"name": "Mattia"
},
"person1": {
"#id": "value1",
"name": "Mattia"
},
"subPersons": [
{
"#id": "value2",
"name": "Luca",
"key": {
"#id": "value1",
"name": "Mattia"
}
},
{
"#id": "value1",
"name": "Mattia"
},
{
"#id": "value3",
"subsubPersons": [
{
"again": {
"#id": "value2",
"name": "Luca",
"key": {
"#id": "value1",
"name": "Mattia"
}
}
}
]
}
],
"key": {
"subKey": {
"#id": "value1",
"name": "Mattia"
}
}
}
]
I'm using this class to replace values:
import UIKit
import Alamofire
import AlamofireObjectMapper
import ObjectMapper
import SwiftyJSON
import SwiftDate
import Async
class FindAndReplace {
var ids = Dictionary<String, JSON>()
var dictChanged = Dictionary<String, JSON>()
var isDictInit: Bool = false
/*
* Find and Replace
*/
func findAndReplace (json: JSON) -> JSON {
findJSOGids(json)
let replaced = replaceJSOGrefs(json, ids: ids)
return replaced
}
/*
* Find "#id" keys and map values related
*/
func findJSOGids (value: JSON) {
for (key, subJson): (String, JSON) in value {
if (key == "#id") {
let mValueForKey = value[key].stringValue
ids[mValueForKey] = value
}
if (subJson.type == Type.Dictionary || subJson.type == Type.Array) {
findJSOGids(subJson)
}
}
}
/*
* Replace "#ref" keys with fields mapped in ids
*/
func replaceJSOGrefs (var value: JSON, var ids: Dictionary<String, JSON>) -> JSON {
if (value.type == Type.Dictionary) {
var result = Dictionary<String, JSON> ()
for (key, subJson): (String, JSON) in value {
if (key == "#ref") {
let mValueForKey = value[key].stringValue
var isReplaced = false
while (isReplaced == false) {
for (idKey, _): (String, JSON) in ids[mValueForKey]! {
if (idKey == "#ref") {
print("found a #ref in dictionary")
let dictValueReplaced = replaceJSOGrefs(ids[mValueForKey]!, ids: ids)
ids.updateValue(dictValueReplaced, forKey: mValueForKey)
}
}
}
return ids[mValueForKey]!
} else {
result[key] = replaceJSOGrefs(subJson, ids: ids)
}
}
return JSON(result)
} else if (value.type == Type.Array) {
var result = [JSON]()
for (_, subJson): (String, JSON) in value {
result.append(replaceJSOGrefs(subJson, ids: ids))
}
return JSON(result)
} else {
return value
}
}
}
It works but it misses some #ref values.
Can someone please help me?
Thanks in advance.
Edit
I'm using ObjectMapper to map objects.
I think that find-replace approach won't be as efficient since you'll have to do many passes on your data (until you can't find any #ref strings).
You should probably leverage the fact that your JSON models reference types semantics (as oppose to value types) and parse it as such, keeping #ref in the parsed objects as faulted references. Every object you parse you should add in the cache that can be referenced by #id. Then in the second pass you'll go through your cache rewiring each reference to using the cache you just built as a lookup table.
If every model implements following protocol
protocol RefObject {
func updateReferences(using cache: [String: RefObject])
}
you can implement it per-model to have a custom rewiring logic per each model class. Here are few examples of such model classes:
For a wildcard represented by just {"#ref": "xxx"} in JSON I'd create a pointer class that would simply point to the referred object.
class Pointer: RefObject {
let referredId: String
var referred: RefObject!
init(referedId: String) {
self.referredId = referredId
}
func updateReferences(using cache: [String : RefObject]) {
self.referred = cache[referredId]
}
}
For a person you can implement something similar to
class Person: RefObject {
let id: String
let name: String
var otherId: String?
var other: Person?
init(id: String, name: String, otherId: String?) {
self.id = id
self.name = name
self.otherId = otherId
}
func updateReferences(using cache: [String : RefObject]) {
other = otherId.flatMap{ cache[$0] as? Person }
}
}
(this assumes that person can have {"id": "xx", "name": "xx", "other": {"#ref": "xx"}} where "other" is other is optional
This is a general approach and not a particular implementation, but it would be very domain specific depending on your needs.
Update there is a similar protocol called JSON API (misleading name IMO, but it utilizes the same approach of referencing JSON objects by id). Here is an implementation in Swift: https://github.com/wvteijlingen/spine it might be worth checking it out
I have a callback function which retrieves stuff from a url however the information doesn't get stored into class variables.
var counts:Int?
var imagesComments : [NSArray] = []
loader.getFeed("1", completionHandler: { response in
if let dataArr = response["content"] as? NSArray{
for downloaded_images in dataArr{
if let image = downloaded_images as? NSDictionary{
let url = image["url"] as? String
// Get images and load into images
if let comments = image["comments"] as? NSArray{
dispatch_async(dispatch_get_main_queue()) {
self.imagesComments.append(comments)
}
}
self.loader.downloadImage(NSURL(string: self.baseUrl+url!)!, completionHandler: { response in
dispatch_async(dispatch_get_main_queue()) {
self.images.append(response)
self.pileView?.reloadContent()
}
})
}
}
}
})
After this code is run, I have a place where I print imagesComments and counts and both are empty/nil
JSON File:
{
"success": true,
"message": null,
"content": [{
"url": "6\/image_2.png",
"date_added": "2015-12-02 22:43:05",
"comments": ["Awesome Pic", "WOOHOOOOO THIS IS GREAT"],
"likes": []
}, {
"url": "2\/image_5.png",
"date_added": "2015-12-01 06:43:48",
"comments": ["EhHHHH"],
"likes": []
}]
}
I am beginner in swift language and currently i am developing some App (Game).
But i have problem when casting AnyObject to String or else, which is always give warning "Cast from [SKNode] to unrelated type 'String' always fails"
Here my code
var facets = [AnyObject]()
init () {
facets = [
[
"id": "1",
"lang": ["id": "Memori", "en": "Memory"]
],
[
"id": "2",
"lang": ["id": "Kecepatan Berpikir", "en": "Speed"]
],
[
"id": "3",
"lang": ["id": "Fungsi Eksekutif", "en": "Control"]
],
[
"id": "4",
"lang": ["id": "Konsentrasi", "en": "Attention"]
],
[
"id": "5",
"lang": ["id": "Pemecahan Masalah", "en": "Problem Solving"]
]
]
}
func findFacetUsingId(id: String?) -> String? {
if let id = id {
for value in facets {
var facet_id: String = value["id"] as! String
if id == facet_id {
var names: Dictionary<String, String> = value["lang"] as! Dictionary<String, String>
return names[Lang.ID]
}
}
}
return nil
}
Here the screenshot,
By the way, I got success when using this code
var facet_id: String = value.objectForKey("id") as! String
instead of
var facet_id: String = value["id"] as! String
But the App to be slow (very very slow)
Thank you in advance
Actually your code works if you just do a little change:
func findFacetUsingId(id: String?) -> String? {
if let id = id {
for value in facets {
var facet_id: String = value["id"] as! String
if id == facet_id {
var names: Dictionary<String, String> = value["lang"] as! Dictionary<String, String>
return names["id"]
}
}
}
return nil
}