CoreData Exc_bad_access code=1 with Sub Entity - ios

I need your help, I still have an beautiful EXC_BAD_ACCESS error on this line self.clinic!.copyWithDTO(clinicDTO).
Console : po self.clinic
Optional(<NSManagedObject: 0x7a7bd240> (entity: Clinic; id: 0x7a7bbe10 <x-coredata://7B916AD3-1989-426F-932F-0A62C755F2B4/Clinic/p1> ; data: <fault>))
I'm trying to fill properties on a sub entity in CoreData model by giving a DTO.
#objc(UserProfile)
class UserProfile: BaseStoredObject {
override class func entityName() -> String {
return "UserProfile"
}
override func copyWithDTO(dto: BaseDTO) {
if let userProfileDTO = dto as? UserProfileDTO {
self.id = userProfileDTO.id
self.login = userProfileDTO.login
self.password = userProfileDTO.password
self.lastname = userProfileDTO.lastname
self.email = userProfileDTO.email
self.createdOn = userProfileDTO.createdOn
self.gender = userProfileDTO.gender
self.userProfileType = userProfileDTO.userProfileType
self.firstname = userProfileDTO.firstname
self.fullname = userProfileDTO.fullname
if let clinicDTO = userProfileDTO.clinic {
if let unwrappedClinic = self.clinic {
unwrappedClinic.copyWithDTO(clinicDTO)
} else {
self.clinic = Clinic(entity: NSEntityDescription.entityForName("Clinic", inManagedObjectContext: self.managedObjectContext!)!, insertIntoManagedObjectContext: self.managedObjectContext)
self.clinic!.copyWithDTO(clinicDTO) // !! EXC_BAD_ACCESS (code=1, address=0x....)
}
}
} else {
fatalError("copyWithDTO: must be sent with UserProfileDTO")
}
}
}
#objc(Clinic)
class Clinic: BaseStoredObject {
override class func entityName() -> String {
return "Clinic"
}
override func copyWithDTO(dto: BaseDTO) {
if let clinicDTO = dto as? ClinicDTO {
self.id = clinicDTO.id
self.name = clinicDTO.name
self.phoneNumber = clinicDTO.phoneNumber
self.phoneNumber24hours = clinicDTO.phoneNumber24hours
self.nurseContactPhoneNumber = clinicDTO.nurseContactPhoneNumber
self.emergencyPhoneNumber = clinicDTO.emergencyPhoneNumber
self.internalClinicId = clinicDTO.internalClinicId
self.webserviceURL = ""
} else {
fatalError("copyWithDTO: must be sent with ClinicDTO")
}
}
}
extension UserProfile {
#NSManaged var id: String?
#NSManaged var login: String?
#NSManaged var password: String?
#NSManaged var lastname: String?
#NSManaged var firstname: String?
#NSManaged var email: String?
#NSManaged var createdOn: NSDate?
#NSManaged var userProfileType: NSNumber?
#NSManaged var gender: NSNumber?
#NSManaged var fullname: String?
#NSManaged var clinic: Clinic?
}
What I'm doing wrong ?
Thank you ;)
EDIT: BaseStoredObject is a subclass of NSManagedObject

Related

How to filter other parent of child with Vapor?

I have this request:
router.get("/fetchOngoingReleases") { (request) -> Future<[ReleaseOut]> in
return Release.query(on: request).filter(\.inprogress == true).all().map { releases in
var result: [ReleaseOut] = []
for r in releases {
var pageEvents: [Event] = []
let num = r.releaseUsers.query(on: request).filter(\.user.fbId ~~ "something").count()
var needAuthentication: Bool
if num == 0 {
needAuthentication = true
} else {
needAuthentication = false
}
let rOut = ReleaseOut(fbId: r.fbId, name: r.name, purpose: r.purpose, needAuthentication: needAuthentication)
result.append(rOut)
}
return result
}
}
}
It says I can not access (???) releaseUser.user.fbId in the query?
Here the data model:
and in code
final class Release: Content {
var id: Int?
var fbId: String
var inprogress: Bool?
var name: String
var purpose: String
/// Creates a new `Release`.
init(id: Int? = nil, fbId: String, name: String, purpose: String = "normal selling") {
self.id = id
self.fbId = fbId
self.name = name
self.purpose = purpose
}
}
extension Release {
var releaseUsers: Children<Release, ReleaseUser> {
return children(\.releaseId)
}
}
final class ReleaseUser: Content {
var id: Int?
var releaseId: Release.ID
var userId: User.ID
init(id: Int? = nil, releaseId: Release.ID, userId: User.ID) {
self.id = id
self.releaseId = releaseId
self.userId = userId
}
}
extension ReleaseUser {
var user: Parent<ReleaseUser, User> {
return parent(\.userId)
}
}
final class User: Content {
var id: Int?
var fbId: String
var name: String
init(id: Int? = nil, fbId: String, name: String) {
self.id = id
self.fbId = fbId
self.name = name
}
}
Ok so there are several things going on here, but the main concept is that you can't just jump across different tables like that - you need to use a JOIN to join the ReleaseUser table to the User table so you can then query on the fbId
Try changing your query to:
Release.query(on: request).filter(\.inprogress == true).join(\ReleaseUser.releaseId, to:\Release.Id).join(\ReleaseUser.userId, to:\User.Id).alsoDecode(User.self).all()
The alsoDecode will give you a tuple with the first position containing your original Release instance and the second containing the corresponding User instance. So, fbId should be available as:
r.1.fbId
In your case.

Indentifiable Protocol extension for SwiftUI list data

I was experimenting with SwiftUI and came across a problem while implementing the data model for one of my List. My plan was to create a protocol CardProtocol as the data protocol for the elements of my lists and then have a CoreData implementation of the protocol as well as a dummy one for unit testing and Canvas use. If you are using a data collection in SwiftUI List the single elements need to conform to the Identifiable protocol.
The code looks like this:
import SwiftUI
import Combine
final class CardsModel: BindableObject {
var cards: [CardProtocol] = []
let didChange = PassthroughSubject<CardsModel, Never>()
}
protocol CardProtocol: Identifiable {
var id: Int { get set }
var firstName: String? { get set }
var lastName: String? { get set }
var email: String? { get set }
var phone: String? { get set }
}
This will not even compile as the Identifiable protocol has 2 associated types which needs to be specified if the protocol is to be used for a variable definition.
/// A type that can be compared for identity equality.
public protocol Identifiable {
/// A type of unique identifier that can be compared for equality.
associatedtype ID : Hashable
/// A unique identifier that can be compared for equality.
var id: Self.ID { get }
/// The type of value identified by `id`.
associatedtype IdentifiedValue = Self
/// The value identified by `id`.
///
/// By default this returns `self`.
var identifiedValue: Self.IdentifiedValue { get }
}
The exact error being error: protocol 'CardProtocol' can only be used as a generic constraint because it has Self or associated type requirements.
Now ID is not an issue and can be fixed, but IdentifiedValue it's by nature different in the CoreData and the dummy implementation.
The only reasonable solution that I found was to remove compliance to Identifiable from the protocol and reintroduce it later in the View using cardsModel.cards.identified(by: \.id). Is there any better way out of this, that let me keep the Identifiable compliance at protocol level?
The only solution, beside adding Identifiable through .identified(by: \.id), is using the type erasure pattern. This uses 3 classes to box and hide the associated type and allows then to declare an array of AnyCard objects. The implementation is quite bulky and probably not worth it for my problem. But here it goes:
final class CardsModel<IdentifiedValue:CardProtocol>: BindableObject {
var cards: [AnyCard<IdentifiedValue>] = []
let didChange = PassthroughSubject<CardsModel, Never>()
}
protocol CardProtocol: Identifiable{
var id: Int32 { get set }
var firstName: String? { get set }
var lastName: String? { get set }
var email: String? { get set }
var phone: String? { get set }
}
struct TestCard: CardProtocol {
var id: Int32
var firstName: String?
var lastName: String?
var email: String?
var phone: String?
}
extension CardsModel where IdentifiedValue == TestCard {
convenience init(cards: [TestCard]) {
self.init()
self.cards = cards.map({ (card) -> AnyCard<TestCard> in
return AnyCard(card)
})
}
}
private class _AnyCardBase<IdentifiedValue>: CardProtocol {
init() {
guard type(of: self) != _AnyCardBase.self else {
fatalError("_AnyCardBase<Model> instances can not be created; create a subclass instance instead")
}
}
var id: Int32 {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
var firstName: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
var lastName: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
var email: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
var phone: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
}
private final class _AnyCardBox<Concrete: CardProtocol>: _AnyCardBase<Concrete.IdentifiedValue> {
var concrete: Concrete
init(_ concrete: Concrete) {
self.concrete = concrete
}
override var id: Int32 {
get {
return concrete.id
}
set {
concrete.id = newValue
}
}
override var firstName: String? {
get {
return concrete.firstName
}
set {
concrete.firstName = newValue
}
}
override var lastName: String? {
get {
return concrete.lastName
}
set {
concrete.lastName = newValue
}
}
override var email: String? {
get {
return concrete.email
}
set {
concrete.email = newValue
}
}
override var phone: String? {
get {
return concrete.phone
}
set {
concrete.phone = newValue
}
}
}
final class AnyCard<IdentifiedValue>: CardProtocol {
private let box: _AnyCardBase<IdentifiedValue>
init<Concrete: CardProtocol>(_ concrete: Concrete) where Concrete.IdentifiedValue == IdentifiedValue {
box = _AnyCardBox(concrete)
}
var id: Int32 {
get {
return box.id
}
set {
box.id = newValue
}
}
var firstName: String? {
get {
return box.firstName
}
set {
box.firstName = newValue
}
}
var lastName: String? {
get {
return box.lastName
}
set {
box.lastName = newValue
}
}
var email: String? {
get {
return box.email
}
set {
box.email = newValue
}
}
var phone: String? {
get {
return box.phone
}
set {
box.phone = newValue
}
}
}
//NSManagedObject extention
extension Card:CardProtocol {}
A simple type-eraser solves that problem.
final class CardsModel: BindableObject {
var cards: [AnyCardModel] = [AnyCardModel]()
let didChange = PassthroughSubject<CardsModel, Never>()
}
protocol CardProtocol: Identifiable {
var id: Int { get set }
var firstName: String? { get set }
var lastName: String? { get set }
var email: String? { get set }
var phone: String? { get set }
}
class AnyCardModel: CardProtocol {
var _id: Int
var _firstName: String?
var _lastName: String?
var _email: String?
var _phone: String?
init<T: CardProtocol>(card: T) {
_id = card.id
_firstName = card.firstName
_lastName = card.lastName
_email = card.email
_phone = card.phone
}
var id: Int {
get { return _id }
set { _id = newValue}
}
var firstName: String? {
get { return _firstName }
set { _firstName = newValue}
}
var lastName: String? {
get { return _lastName }
set { _lastName = newValue}
}
var email: String?{
get { return _email }
set { _email = newValue}
}
var phone: String?{
get { return _phone }
set { _phone = newValue}
}
}

returnsObjectsAsFaults not working as expected

I created a Core Data object as follows:
#objc(Gates)
public class Gates : NSManagedObject {
public class func getFetchRequest() -> NSFetchRequest<Gates> {
let request = NSFetchRequest<Gates>(entityName: "Gates")
request.returnsObjectsAsFaults = false
return request
}
#NSManaged var updatedAt: String
#NSManaged var objectId: String
#NSManaged var identifier: String
#NSManaged var name: String
#NSManaged var address: String
#NSManaged var dueDate: String
#NSManaged var productionCode: String
#NSManaged var locationCountry: String
#NSManaged var locationCity: String
#NSManaged var locationBuilding: String
#NSManaged var locationLevel: String
#NSManaged var locationRoom: String
#NSManaged var locationRange: String
#NSManaged var isFavorite: Bool
public func setGateData(gateDict: [String: Any]) {
updatedAt = gateDict["updatedAt"] as? String ?? ""
objectId = gateDict["objectId"] as? String ?? ""
identifier = gateDict["identifier"] as? String ?? ""
name = gateDict["name"] as? String ?? ""
isFavorite = gateDict["isFavorite"] as? Bool ?? false
address = gateDict["address"] as? String ?? ""
dueDate = gateDict["dueDate"] as? String ?? ""
productionCode = gateDict["productionCode"] as? String ?? ""
locationCountry = gateDict["locationCountry"] as? String ?? ""
locationCity = gateDict["locationCity"] as? String ?? ""
locationBuilding = gateDict["locationBuilding"] as? String ?? ""
locationLevel = gateDict["locationLevel"] as? String ?? ""
locationRoom = gateDict["locationRoom"] as? String ?? ""
locationRange = gateDict["locationRange"] as? String ?? ""
}
}
I also set this up in the xcdatamodeld:
Now, after I have saved the object in core data and I'm using the getFetchRequest() method that is part of the class which sets
request.returnsObjectsAsFaults = false on the request but I still getting the following result when I try to print the fetched objects:
<Gates: 0x60c0000959a0> (entity: Gates; id: 0xd000000005e40000 <x-
coredata://B9C33A5D-BF96-433A-9186-F51AA253F488/Gates/p377> ; data: <fault>)
As you can see in this case the data is still data: <fault>.
Why is the object parameters are not retrieved even though I set request.returnsObjectsAsFaults = false? What am I missing?
I'm having this issue and I found in my case instead of using the objects value in line, I initialize a variable with it first and then use that variable.
I would love to know if this is a Core Data bug or if I'm doing something wrong.
public class Person: NSManagedObject, Identifiable {
#NSManaged public var firstName: String
#NSManaged public var lastName: String
#NSManaged public var emailAddress: String
}
This does not work all the time:
CoreDataManager.shared.persistentContainer.performBackgroundTask{ context in
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
let email = "123Tester#gmail.com"
let request = Person.getPersonWith(email: email)
request.returnsObjectsAsFaults = false //DOES NOT WORK
if let person = try context.fetch(request).first{
print(person.fullName)
}
} catch{
fatalError()
}
}
However this does
CoreDataManager.shared.persistentContainer.performBackgroundTask{ context in
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
let email = "123Tester#gmail.com"
let request = Person.getPersonWith(email: email)
if let person = try context.fetch(request).first{
let fullName = person.fullName
print(fullName)
}
} catch{
fatalError()
}
}

NSPredicate object model array filter not working its crashing in Swift 4.0

I'm working in swift 4.0, i have model object in MutableArray,
NSPredicate not working.
//Here is code
//Model class:
class Modelclass: NSObject
{
var firstName:String!
var lastName:String!
}
// Viewcontroller doing predicate:
let ary = NSMutableArray()
let userModel = Modelclass()
userModel.firstName = "Arrya"
userModel.lastName = "stark"
ary.add(userModel)
let commitPredicate = NSPredicate(format: "firstName == %#", "Arrya")
let resultAry = ary.filtered(using: commitPredicate)
print(resultAry)
I solved the problem,
Solution 1:
//I just added below code in model class
extension Modelclass {
#objc override func value(forKey key: String) -> Any? {
switch key {
case "firstName":
return firstName
case "lastName":
return lastName
default:
return nil
}
}
}
Solution 2:
Add #objc before variable:
class Modelclass: NSObject
{
#objc var firstName:String!
#objc var lastName:String!
}
var is introduced for Mutable Content/Variables
In swift you can use filter alternative to NSPredicate
Try Like this:
//Model class:
class Modelclass
{
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
// Viewcontroller doing predicate:
var ary:[Modelclass] = []
let userModel = Modelclass(firstName: "Arrya", lastName: "stark")
ary.append(userModel)
let resultAry = ary.filter{ $0.firstName == "Arrya" }
print(resultAry)

In Swift, how can I quickly set my instance variables?

public class User {
var id: Int
var fb_id: String?
var first_name: String?
var last_name: String?
var age: Int?
var distance: Int?
var login_at_pretty: String?
var login_at: Int?
var profile: Profile?
init(id: Int, fb_id: String?, first_name: String?, last_name: String?, age: Int?, distance: Int?, login_at_pretty: String?, login_at: Int?, profile: Profile?){
self.id = id
if let fb_id = fb_id {
self.fb_id = fb_id
}
if let first_name = first_name {
self.first_name = first_name
}
if let last_name = last_name {
self.last_name = last_name
}
if let age = age {
self.age = age
}
if let distance = distance {
self.distance = distance
}
if let login_at_pretty = login_at_pretty {
self.login_at_pretty = login_at_pretty
}
if let login_at = login_at {
self.login_at = login_at
}
if let profile = profile {
self.profile = profile
}
}
}
Is this the quickest way to do it? I feel like I'm over typing.
For your class, you're doing a lot of if-let statements that don't provide anything useful. ie:
if let fb_id = fb_id {
self.fb_id = fb_id
}
fb_id and self.fb_id are optionals, so the binding isn't necessary. Just do:
self.fb_id = fb_id
On that note however, if you're not planning on subclassing, using a struct would provide a memberwise initializer automagically:
public struct User {
var id: Int
var fb_id: String?
var first_name: String?
var last_name: String?
var age: Int?
var distance: Int?
var login_at_pretty: String?
var login_at: Int?
var profile: Profile?
}
Check out Memberwise Initializers for Structure Types in the swift docs

Resources