[Core Data]: data: <fault> - swift

The questions parameter in the function is full, but I cannot transfer assignments to questionsCD.questions?.questionList.
The id and title variables are not nil. Those variables are working in a healthy way.
In Console output, questions return nil.
I cannot assign the questionList variable in the QuestionsNSSecureCoding class, so I think it returns nil. Why ?
You can examine the Core Data Entity image to see the questionsCD.questions?.questionList in more detail.
Core Data Save Function:
func saveSelectedQuestion(questionTitle: String, id: String, questions: [QuestionList]) {
let questionsCD = QuestionCD(context: persistentContainer.viewContext)
questionsCD.title = questionTitle
questionsCD.id = id
questionsCD.questions?.questionList = questions
print("nil test: \(questionsCD.questions?.questionList ?? [])")
do {
try persistentContainer.viewContext.save()
} catch let error {
print("Failed to save selected category: \(error.localizedDescription)")
}
}
Core Data Get Function:
func getSelectedQuestion(questionID: String) -> [QuestionCD] {
let fetchRequest: NSFetchRequest<QuestionCD> = QuestionCD.fetchRequest()
let search = NSPredicate(format: "id == %#", questionID)
print("search: \(search)")
fetchRequest.predicate = search
fetchRequest.returnsObjectsAsFaults = false
print("request predicate: \(String(describing: fetchRequest.predicate))")
do {
return try persistentContainer.viewContext.fetch(fetchRequest)
} catch let error {
print("get hata: \(error.localizedDescription)")
return []
}
}
Console Output:
selectedQuestionCD [<QuestionCD: 0x2800e8e60> (entity: QuestionCD; id: 0x9d00d28ec7359eb0 <x-coredata://089E80AC-0E4F-4303-BF8F-47C31EC70ED4/QuestionCD/p2>; data: {
id = "agustos_test_1";
questions = nil;
title = "A\U011fustos Test 1";
})]
Core Data Entity:
Questions NSSecure Coding:
public class QuestionsNSSecureCoding: NSObject, NSSecureCoding {
public static var supportsSecureCoding: Bool = true
var questionList: [QuestionList]
required init(questions: [QuestionList]) {
self.questionList = questions
}
public func encode(with coder: NSCoder) {
coder.encode(questionList, forKey: "questionList")
}
required public init?(coder: NSCoder) {
questionList = coder.decodeObject(of: NSArray.self, forKey: "questionList") as? Array<QuestionList> ?? []
}
}
Questions Value Transformer:
#objc(QuestionsValueTransformer)
final class QuestionsValueTransformer: NSSecureUnarchiveFromDataTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: QuestionsValueTransformer.self))
override static var allowedTopLevelClasses: [AnyClass] {
return [QuestionsNSSecureCoding.self]
}
public static func register() {
let transformer = QuestionsValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
My Custom Model:
class QuestionContainer: Codable {
var questionCategories: [Question]
init(questionCategories: [Question]) {
self.questionCategories = questionCategories
}
}
class Question: Codable, Identifiable {
var title: String
var id: String
var questions: [QuestionList]
init(title: String, id: String, questions: [QuestionList]) {
self.title = title
self.id = id
self.questions = questions
}
}
class QuestionList: Codable, Identifiable {
var id: String
init(id: String) {
self.id = id
}
}

I think the problem is that questionCD.questions is nil, so the line
questionsCD.questions?.questionList = questions
does nothing. You need to create an instance of QuestionsNSSecureCoding with the correct questions array, and assign that to the questions property:
questionsCD.questions = QuestionsNSSecureCoding(questions: questions)

Related

How to use generics and inheritance to parse an output?

I'm programming a network call for a backend in NestJs which has pagination enabled. The output of the service has the following structure:
{
"items": [{
"id": "26c26ecb-0763-4813-b489-3c6ad94084a9",
"body": "Test body",
"subTitle": "SubTitle Test",
"author": "Test Author",
"createAt": "2023-01-12T07:04:58.480Z",
"title": "Temporary Title",
"updateAt": "2023-01-12T07:04:58.480Z"
}],
"meta": {
"itemsPerPage": 10,
"totalPages": 2,
"currentPage": 1,
"totalItems": 19,
"itemCount": 10
},
"links": {
"previous": "",
"first": "\/post\/public?limit=10",
"next": "\/post\/public?page=2&limit=10",
"last": "\/post\/public?page=2&limit=10"
}
}
The Meta and Links atribute are fairly straight through so they have no problems. The issue that i'm having is the items list. The objects data structures that are inside can be different, there can be News, Posts, Events.
All of this objects have some properties in common so a BaseEntity Model was created. Therefore all models inherit from BaseEntity.
I dont want to create a class for each of this outputs. I want to declare a generic class that receives a generic an parses it accordingly.
My BaseCrudEntity model and protocol are designed like this:
public protocol BaseCrudEntityParsingProtocol: AnyObject
{
var id: String { get set }
var createAt: Date { get set }
var updateAt: Date { get set }
var deletedAt: Date? { get set }
var isActive: Bool { get set }
init()
init?(info: Any?)
static func parseList(info: Any?) -> [BaseCrudEntityParsingProtocol]?
}
public extension BaseCrudEntityParsingProtocol
{
static func parseList(info: Any?) -> [BaseCrudEntityParsingProtocol]? {
return nil
}
}
public class BaseCrudEntity: BaseCrudEntityParsingProtocol
{
public var createAt: Date = Date()
public var updateAt: Date = Date()
public var deletedAt: Date?
public var isActive: Bool = true
public var id: String = ""
enum CodingKeysBase: String {
case createAt = "createAt"
case updateAt = "updateAt"
case deletedAt = "deletedAt"
case isActive = "isActive"
case id = "id"
}
required public init() { }
required public init?(info: Any?) {
guard let anyInfo = info,
let info = anyInfo as? NSDictionary else { return nil }
self.createAt = info.parseString(forKeyPath: CodingKeysBase.createAt.rawValue).toDate()
self.updateAt = info.parseString(forKeyPath: CodingKeysBase.updateAt.rawValue).toDate()
self.deletedAt = info.parseStringOptional(forKeyPath: CodingKeysBase.deletedAt.rawValue)?.toDateOptional()
self.isActive = info.parseBool(forKeyPath: CodingKeysBase.isActive.rawValue)
self.id = info.parseString(forKeyPath: CodingKeysBase.id.rawValue)
}
}
And an example of a specific model which will have pagination looks like this:
// MARK: - Post
public class Post: BaseCrudEntity
{
public var title: String?
public var subTitle: String?
public var author: String?
public var body: String?
enum CodingKeys: String, CodingKey {
case title = "title"
case subTitle = "subTitle"
case author = "author"
case body = "body"
}
required public init() {
super.init()
}
required public init?(info: Any?) {
super.init(info: info)
guard let anyInfo = info,
let info = anyInfo as? NSDictionary else { return nil }
self.title = info.parseStringOptional(forKeyPath: CodingKeys.title.rawValue)
self.subTitle = info.parseStringOptional(forKeyPath: CodingKeys.subTitle.rawValue)
self.author = info.parseStringOptional(forKeyPath: CodingKeys.author.rawValue)
self.body = info.parseStringOptional(forKeyPath: CodingKeys.body.rawValue)
}
public static func parseList(info: Any?) -> [Post]? {
guard let anyInfo = info,
let rawList = anyInfo as? [NSDictionary] else { return nil }
return rawList.compactMap(Post.init)
}
}
I've created the following class so that I can pass a generic value and parse it accordingly, the problem that i'm having is that the items list returns nil, as if the parsing cannot be done successfully or is having problems. This task could be done using the Codable protocol but due to the codebase design it cannot be done at this time.
public class PaginatedList<Body: BaseCrudEntity> where Body: BaseCrudEntity
{
public var items: [Body]?
public var meta: PaginatedListMeta?
public var links: PaginatedListLinks?
enum CodingKeys: String, CodingKey {
case meta = "meta"
case links = "links"
case items = "items"
}
public init() {
}
public init(items: [Body]?, meta: PaginatedListMeta?, links: PaginatedListLinks?) {
self.meta = meta
self.links = links
}
public init?(info: Any?) {
guard let anyInfo = info,
let info = anyInfo as? NSDictionary else { return nil }
self.meta = PaginatedListMeta(info: info[CodingKeys.meta.rawValue])
self.links = PaginatedListLinks(info: info[CodingKeys.links.rawValue])
self.items = Body.parseList(info: info[CodingKeys.items.rawValue]) as? [Body]
}
}
I've tried a couple of solutions but none seem to work.

How to load data in the collectionview from JSON to Swift by using Alamofire and Kingfisher

I need to get the data from already made JSON file to the Swift. By using MVVM desing I wrote this code in the Repo class
func loadFoods() {
AF.request("http://example.com/foods/getAllFoods.php",method: .get).response { response in
if let data = response.data {
do{
let result = try JSONDecoder().decode(ProductsResponse.self, from: data)
if let list = result.foods {
self.foodList.onNext(list)
}
}catch{
print(error.localizedDescription)
}
}
}
}
Here's the code from View Model class:
class HomeViewModel {
var foodList = BehaviorSubject <[Foods]>(value: [Foods]())
var frepo = FoodsDaoRepository()
init() {
loadFoods()
foodList = frepo.foodList
}
func loadFoods() {
frepo.loadFoods()
}
func loadPersons(){
prepo.loadPersons()
}
and I wrote this code in the ViewContoller class:
override func viewDidLoad() {
super.viewDidLoad()
searchTextField.delegate = self
collectionView.delegate = self
collectionView.dataSource = self
let _ = viewModel.foodList.subscribe(onNext: { list in
self.foodsList = list
DispatchQueue.main.async {
self.collectionView.reloadData()
}
})
override func viewWillAppear(_ animated: Bool) {
viewModel.loadFoods()
}
Foodlist variable takes the data from Food class:
class Foods: Codable {
var id : Int?
var name : String?
var image : String?
var price : Int?
var category : String?
init(id: Int?, name: String?, image: String?, price: Int?, category: String?) {
self.id = id
self.name = name
self.image = image
self.price = price
self.category = category
}
}
But it didn't help to get the data from JSON to the CollectionView. It just shows empty collection view cells.
Also how can I get an image by using Kingfisher and Alamofire?
I tried to explain the problem and wrote a code to expand my question

Value of a child from mirror introspection not conformed to protocol anymore

I am trying to understand swift's inflection capabilities.
I have a parent Passport class whose child (User) implements a protocol Clonable, however when introspecting the child value, it fails the check child.value is Clonable.
Can someone explain this?
extension Clonable {
func clone() -> Self? {
if let me = self as? SimpleInit {
let new = type(of: me).init()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let kp = child.label, let new = new as? NSObject {
if child.value is Clonable, let value = child.value as? Clonable { // this should be true
print("cloning \(child.value) for keypath \(kp)")
new.setValue(value.clone(), forKeyPath: kp)
} else {
print("not cloning \(child.value) for keypath \(kp)")
new.setValue(child.value, forKeyPath: kp)
}
}
}
return new as? Self
}
return nil
}
}
class Passport: NSObject, Clonable, SimpleInit, CustomReflectable {
var customMirror: Mirror {
return Mirror(self, children: ["user": user])
}
#objc var user: User?
required override init() {
}
func printMe() {
user?.printMe()
}
}
class User: NSObject, Clonable, SimpleInit, CustomReflectable {
var customMirror: Mirror {
return Mirror(self, children: ["name": name])
}
#objc var id: Int
#objc var name: String?
required override init() {
print("init user")
id = Int(arc4random())
}
func printMe() {
print("id \(id) name \(name)")
}
}
let passport = Passport()
passport.user = User()
passport.user?.name = "John"
let clone = passport.clone()
passport.printMe()
clone?.printMe()
This is the output:
init user // should be called second time when user gets cloned.
not cloning Optional(<__lldb_expr_92.User: 0x6000039d6420>) for keypath user
id 2046302380 name Optional("John")
id 2046302380 name Optional("John")

Realm accessed from incorrect thread occasional

I have this function
class func addCVals(_ criteres: [[AnyHashable: Any]], _ type: String) {
DispatchQueue.global(qos: .background).async {
autoreleasepool {
if criteres.count > 0 {
if let realm = DBTools.getRealm() {
do {
try realm.transaction {
let oldValues = CriteresVal.objects(in: realm, where: "type = '\(type)'")
if oldValues.count > 0 {
realm.deleteObjects(oldValues)
}
for critere in criteres {
let cval = CriteresVal(critere, type)
if let c = cval {
realm.addOrUpdate(c)
}
}
}
} catch {
DebugTools.record(error: error)
}
realm.invalidate()
}
}
}
}
}
The request that get oldValues occasionally cause an error
Realm accessed from incorrect thread
I don't understand why as I get a new Realm before with this lines:
if let realm = DBTools.getRealm()
My function getRealm:
class func getRealm() -> RLMRealm? {
if !AppPreference.lastAccount.elementsEqual("") {
let config = RLMRealmConfiguration.default()
do {
return try RLMRealm(configuration: config)
} catch {
DebugTools.record(error: error)
DispatchQueue.main.async {
Notifier.showNotification("", NSLocalizedString("UNKNOWN_ERROR_DB", comment: ""), .warning)
}
}
}
return nil
}
CriteresVal is an RLMObject that is composed of this:
#objcMembers
public class CriteresVal: RLMObject {
dynamic var cvalId: String?
dynamic var type: String?
dynamic var text: String?
dynamic var compositeKey: String?
override public class func primaryKey() -> String {
return "compositeKey"
}
private func updatePrimaryKey() {
self.compositeKey = "\(self.cvalId ?? "")/\(self.type ?? "")"
}
required init(_ cvalue: [AnyHashable: Any]?, _ type: String) {
super.init()
if let values = cvalue {
if let cvalId = values["id"] as? String {
self.cvalId = cvalId
} else if let cvalId = values["id"] as? Int {
self.cvalId = "\(cvalId)"
}
self.type = type
if let text = values["text"] as? String {
self.text = text
}
}
updatePrimaryKey()
}
func generateDico() -> [String: Any] {
var dicoSortie = [String: Any]()
if let realm = self.realm {
realm.refresh()
}
if let value = cvalId {
dicoSortie["id"] = value
}
if let value = type {
dicoSortie["type"] = value
}
if let value = text {
dicoSortie["text"] = value
}
return dicoSortie
}
}
compositeKey is the primary key which included cvalId and type
Thanks for help.

Typecasting causing struct values to change (Swift)

After downcasting an array of structs, my Variables View window shows that all of the values in my struct have shifted "down" (will explain in a second). But when I print(structName), the values are fine. However, when I run an equality check on the struct, it once again behaves as though my values have shifted.
For example, I am trying to downcast Model A to ModelProtocol. var m = Model A and has the values {id: "1234", name: "Cal"}. When I downcast, m now has the values { id:"\0\0", name:"1234" }.
Actual Example Below:
Models that I want to downcast:
struct PrivateSchoolModel: Decodable, SchoolProtocol {
var id: String
var name: String
var city: String
var state: String
}
struct PublicSchoolModel: Decodable, SchoolProtocol {
var id: String
var name: String
var city: String
var state: String
var latitude: String
var longitude: String
}
Protocol I want to downcast to:
protocol SchoolProtocol {
var id: String { get set }
var name: String { get set }
var city: String { get set }
var state: String { get set }
var longitude: Float { get set }
var latitude: Float { get set }
}
extension SchoolProtocol {
var longitude: Float {
get { return -1.0 }
set {}
}
var latitude: Float {
get { return -1.0 }
set {}
}
}
Downcasting:
guard let downcastedArr = privateSchoolArray as? [SchoolProtocol] else { return [] }
Result (item at index 0) or originalArr:
id = "1234"
name = "Leo High School"
city = "Bellview"
state = "WA"
Result (item at index 0) of downcastedArr:
id = "\0\0"
name = "1234"
city = "Leo High School"
state = "Bellview"
But if I print(downcastArr[0]), it will show:
id = "1234"
name = "Leo High School"
city = "Bellview"
state = "WA"
But if I try originalArray[0].id == downcastArr[0].id, it returns false
My Code with the problem:
class SchoolJSONHandler {
private enum JSONFile: String {
case publicSchool = "publicSchool"
case privateSchool = "privateSchool"
}
private lazy var privateSchoolArray = getPrivateSchools()
private lazy var publicSchoolArray = getPublicSchools()
func getSchoolArray(sorted: Bool, filtered: Bool, by stateAbbreviation: String?) -> [SchoolProtocol] {
var schools = combineArrays()
if sorted {
schools.sort(by: { $0.name < $1.name })
}
if filtered {
guard let abbr = stateAbbreviation else { return [] }
schools = schools.filter {
return $0.state == abbr
}
}
return schools
}
private func combineArrays() -> [SchoolProtocol] {
// EVERYTHING IS FINE IN NEXT LINE
let a = privateSchoolArray
// PROBLEM OCCURS IN NEXT 2 LINES WHEN DOWNCASTING
let b = privateSchoolArray as [SchoolProtocol]
let c = publicSchoolArray as [SchoolProtocol]
return b + c
}
private func getPublicSchools() -> [PublicSchoolModel] {
guard let jsonData = getJSONData(from: .publicSchool) else { return [] }
guard let schools = decode(jsonData: jsonData, using: [PublicSchoolModel].self) else { return [] }
return schools
}
private func getPrivateSchools() -> [PrivateSchoolModel] {
guard let jsonData = getJSONData(from: .privateSchool) else { return [] }
guard let schools = decode(jsonData: jsonData, using: [PrivateSchoolModel].self) else { return [] }
return schools
}
private func getJSONData(from resource: JSONFile) -> Data? {
let url = Bundle.main.url(forResource: resource.rawValue, withExtension: "json")!
do {
let jsonData = try Data(contentsOf: url)
return jsonData
}
catch {
print(error)
}
return nil
}
private func decode<M: Decodable>(jsonData: Data, using modelType: M.Type) -> M? {
do {
//here dataResponse received from a network request
let decoder = JSONDecoder()
let model = try decoder.decode(modelType, from:
jsonData) //Decode JSON Response Data
return model
} catch let parsingError {
print("Error", parsingError)
}
return nil
}
}
And then it is just called in another class by schoolJSONHandler.getSchoolArray(sorted: true, filtered: true, by: "WA")