App is terminating when trying to get an object value - ios

My homework is to get a JSON array and save it to CoreData and then fetch it to a UITableView.
This is my first time working with CoreData and have already saved objects in CoreData, but when fetching I couldn't get an Object from the class I defined but instead I got [NSManagedObject].
So I added #NSManaged on every field in my defined class, but then the app terminates.
Movie.swift
import Foundation
import CoreData
class Movie :NSObject, Codable{
#NSManaged var title:String
#NSManaged var image:String
#NSManaged var rating:Double
#NSManaged var releaseYear:Int
#NSManaged var genre:[String]
init(title:String, image:String, rating:Double, releaseYear:Int, genres:[String]) {
super.init()
self.title = title
self.image = image
self.rating = rating
self.releaseYear = releaseYear
self.genre = genres
}
var title1:String{
get{
return title
}
}
}
The main problem is supposed to be in Movie.swift, I looked it up online and I couldn't find much that could help.
Could it be that I need to add NSManagedObject instead of NSObject?

Some issues
A class representing a Core Data entity must be a subclass of NSManagedObject. Let Xcode create the subclass(es) on your behalf.
A custom init method cannot be used unless it's a convenience initializer calling init(entity:insertInto:) or other designated initializers.
It's not trivial to implement Codable. Please see How to use swift 4 Codable in Core Data
For further information please read the Core Data Programming Guide

If you have a core data model, Xcode can generate the model class for you. Open the xcdatamodel file and in the menu bar, go to Editor -> Create NSManagedObject Subclass.

Related

Array Data Through Different View Controllers Swift

Hey guys I'm working on my first ever app and need some help. My workout tracker app has two classes which you can find below. I have two view controllers hooked up to their own swift files.
Heres my first view controller
Basically what it does is takes the data in the text fields and steppers and turns it into a "Workout" object then appends it to the "WorkoutList" class array.
I've got a print statement setup that prints the Array.count. It shows the correct number in the debug but when I switch views it gets reset to zero.
#IBAction func addToWorkoutList(_ sender: UIButton) {
let workout = Workout(name: workoutName.text!, description: workoutDescription.text!, sets: Int(setStepper.text!)!, reps: Int(repStepper.text!)!)
workoutArrayList.append(workout)
print(workoutArrayList.count)
}
The second view inherits the first views class so that is how I access the "WorkoutArrayList"
class OverViewViewController: NewWorkoutViewController, UITableViewDelegate, UITableViewDataSource {
My app basically allows you to add a workout then produces a graph based on the data you provided. This can make it easy to visualize your gains in the gym. I'm doing this as a learning project so any help on what I should do to build this app would also be greatly appreciated.
Workout Object Class
import Foundation
class Workout {
let workoutName : String
let workoutDescription : String
let numberOfSets : Int
let numberOfReps : Int
init(name : String, description : String, sets : Int, reps : Int) {
workoutName = name
workoutDescription = description
numberOfSets = sets
numberOfReps = reps
}
}
WorkoutList Class
import Foundation
class WorkoutList {
let workoutArray : [Workout] = []
}
Inheriting the class is not what you want to do here. An easy fix for you would be to make workoutArray a static variable so any class can access it at any given time.
static var workoutArray : [Workout] = []
Here is why just inheriting the class doesn't work. When the OverViewViewController loads in to the app, it creates a new instance of the class OverViewViewController and since it inherits NewWorkoutViewController, it also creates a new instance of the NewWorkoutViewController class. You have 2 different instances of NewWorkoutViewController, changing a variable in one of those instances won't change it for any other instances of the class. A static variable, however, is what you are looking for. The array can be changed and accessed any time from any class, you don't even need to inherit. It would work whether you made workoutArray static or workoutArrayList static.
If you have any other questions, feel free to leave a comment.

How to view generated managed object category/extension in XCode Editor

In Core data when I create a data model, I choose to generate the category extension, I understand that this is created in derived data
I want to be able to view this in the XCode code editor but I don't know how I can open it just to view it.
I am using XCode 9 beta 3
The ways I can manage to do this is by typing some thing such as
let m = MenuItem()
let n = m.name
In the code editor and then command clicking on name and then it jumps to the file, or finding the generated extension in Finder and opening from there.
//
// MenuItem+CoreDataProperties.swift
//
//
// Created by Ryan.Heitner on 01/08/2017.
//
// This file was automatically generated and should not be edited.
//
import Foundation
import CoreData
extension MenuItem {
#nonobjc public class func fetchRequest() -> NSFetchRequest<MenuItem> {
return NSFetchRequest<MenuItem>(entityName: "MenuItem")
}
#NSManaged public var available: Bool
#NSManaged public var desc: String?
#NSManaged public var name: String?
#NSManaged public var price: Double
}
The category extension seems buggy to me. Just select "Manual/None", then select an entity or all entities and click on Editor->Create NSManagedObject Subclass, choose your entities and the extensions should be shown under your project root in Xcode.
Unfortunately what you're doing is the only option Xcode provides. It's not good, but you haven't missed anything. I suggest filing a bug with Apple about this. In the meantime, either deal with this as it is, or switch to some other means of generating your properties-- manually maybe, or using mogenerator.

Storing an array of Realm objects inside another Realm object (Swift)

Im creating a music like app. So far I am able to create and save song objects and save them to realm. The song objects are made up of simple "songTitle" and "songArtist" string variables.
I would like to add playlist-like functionality and I believe the best way would be through arrays. The playlist object would contain a "songsInPlaylist" array and that array would be populated with a list of previously created song objects. I have looked over the documentation and I cant get a lead on where to start.
In short, how do you create a realm object that contains an array of other realm objects.
I am using Swift 2.0
Click to see visual representation...
Using array of Realm Objects is simple, just use List container data structure to define to-many relation. Check this example:
class Task: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
dynamic var notes = ""
dynamic var isCompleted = false
}
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let tasks = List<Task>()
}
You can have a look to my sample Todo app using Ream in Github
In mapper,
(map["key"], ArrayTransform<Object>())
"key" is JSON key
"Object" is your custom object

Core Data simple relationship - NSSet

I am trying to play with relationships in xCode and I'm having some problems:
I want to create a simple User to favoriteThing relationship.
I created a model, so every user looks like this:
extension User {
#NSManaged var name: String?
#NSManaged var favoriteThings: NSSet?
}
extension FavoriteThing {
#NSManaged var thingName: String?
#NSManaged var user: User?
}
And I got a tableview conected (so it displays favoriteThing for each user).
The thing is, when I create a new favoriteThing how to add this thing to favoriteThings NSSet that is created in User class?
And what about when I delete this favoriteThing from my table view? (it is managed by fetchedResultsController) How to delete it also from mentioned favoriteThings NSSet?
Any help appreciated!
In the case of a one-to-many relationship, the easiest way to
add an object to the to-many relationship is to set the property
of the inverse to-one relationship:
theFavoriteThing.user = theUser
This automatically updates theUser.favoriteThings to include
theFavoriteThing.
And to remove it just set the inverse to-one relationship to nil:
theFavoriteThing.user = nil

Setting an NSManagedObject relationship in Swift

How does one add an object to a relationship property in an NSManagedObject subclass in Swift?
In Objective-C, when you generate an NSManagedObject subclass in Xcode from the data model, there's an automatically generated class extension which contains declarations like:
#interface MyManagedObject (CoreDataGeneratedAccessors)
- (void)addMySubObject: (MyRelationshipObject *)value;
- (void)addMySubObjects: (NSSet *)values;
#end
However Xcode currently lacks this class generation capability for Swift classes.
If I try and call equivalent methods directly on the Swift object:
myObject.addSubObject(subObject)
...I get a compiler error on the method call, because these generated accessors are not visible.
I've declared the relationship property as #NSManaged, as described in the documentation.
Or do I have to revert to Objective-C objects for data models with relationships?
As of Xcode 7 and Swift 2.0 (see release note #17583057), you are able to just add the following definitions to the generated extension file:
extension PersonModel {
// This is what got generated by core data
#NSManaged var name: String?
#NSManaged var hairColor: NSNumber?
#NSManaged var parents: NSSet?
// This is what I manually added
#NSManaged func addParentsObject(value: ParentModel)
#NSManaged func removeParentsObject(value: ParentModel)
#NSManaged func addParents(value: Set<ParentModel>)
#NSManaged func removeParents(value: Set<ParentModel>)
}
This works because
The NSManaged attribute can be used with methods as well as
properties, for access to Core Data’s automatically generated
Key-Value-Coding-compliant to-many accessors.
Adding this definition will allow you to add items to your collections. Not sure why these aren't just generated automatically...
Yeah that's not going to work anymore, Swift cannot generate accessors at runtime in this way, it would break the type system.
What you have to do is use the key paths:
var manyRelation = myObject.valueForKeyPath("subObjects") as NSMutableSet
manyRelation.addObject(subObject)
/* (Not tested) */
Core Data in Objective C automatically creates setter methods (1):
By default, Core Data dynamically creates efficient public and primitive get and set accessor methods for modeled properties (attributes and relationships) of managed object classes. This includes the key-value coding mutable proxy methods such as addObject: and removes:, as detailed in the documentation for mutableSetValueForKey:—managed objects are effectively mutable proxies for all their to-many relationships.
As things currently stand with Swift in Xcode6-Beta2, you'd have to implement those accessors yourself. For example if you have an unordered to-many relationship, from Way to Node, you'd implement addNodesObject like this:
class Way : NSManagedObject {
#NSManaged var nodes : NSSet
func addNodesObject(value: Node) {
self.mutableSetValueForKey("nodes").addObject(value)
}
}
Key here is that you'd have to use mutableSetValueForKey / mutableOrderedSetValueForKey / mutableArrayValueForKey. On these sets / arrays, you can call addObject and they'll be stored on the next flush.
You can just use a typed Set instead which is far easier. Following the example provided by #Nycen and #lehn0058 in the previous answer, you can just write:
extension PersonModel {
#NSManaged var parents: Set<ParentModel>?
}
And then use the insert and remove methods of the Set.
Expanding on the solution above one to many relationships are NSMutableSet so this allows you to directly add or remove the Person NSManagedObject to the Roles in this case a Person has one Role and Roles have many Person(s)
I have tested this solution under Xcode Beta-3 and this works!
This code takes out the Department to simplify showing the one to one and one to many code required to access Roles from a Person and Persons from a Role.
import CoreData
#objc(Person) class Person: NSManagedObject {
#NSManaged var name: String
//One to One relationship in your Model
#NSManaged var roles: Roles
}
#objc(Roles) class Roles: NSManagedObject {
#NSManaged var role: String
//One to Many relationship in your Model
#NSManaged var persons: NSMutableSet
}
extension Roles {
func addPersonsObject(value: Person) {
self.persons.addObject(value)
}
func removePersonsObject(value: Person) {
self.persons.removeObject(value)
}
func addPersons(values: [Person]) {
self.persons.addObjectsFromArray(values)
}
func removePersons(values: [Person]) {
for person in values as [Person] {
self.removePersonsObject(person)
}
}
}
As of Xcode 8 and Swift 3.0, Xcode now generates accessors for relationships. For example, I have an NSManagedObject class Store, that has a one to many relationship with Items; I've called that relationship SellsItems. The generated class for Store now has the following extension to add and remove from SellsItems. Adding or removing items to the relationship is as simple as calling these functions.
// MARK: Generated accessors for sellsItems
extension Store {
#objc(addSellsItemsObject:)
#NSManaged public func addToSellsItems(_ value: Item)
#objc(removeSellsItemsObject:)
#NSManaged public func removeFromSellsItems(_ value: Item)
#objc(addSellsItems:)
#NSManaged public func addToSellsItems(_ values: NSSet)
#objc(removeSellsItems:)
#NSManaged public func removeFromSellsItems(_ values: NSSet)
}
As you only need to set one side of a relationship for both to be set nowadays, it's particularly simple if you have a 1<->many relationship, e.g. a Department object has multiple Person objects, then you can just use:
aPerson.department = aDepartment
If you check you'll find that aDepartment.people (assuming that is the reciprocal relationship you've set up) will now contain the 'aPerson' Person object.
If the relationship is many<->many then one of the more complex solutions above appears necessary.
Let's say you have the following entities:
Person
Role
Department
In your Person entity, they have a to-many relationship with Role and to-one with Department. Your managed object might look something like this:
class Person : NSManagedObject
{
#NSManaged var roles : Array<Role>
#NSManaged var department : Department
}
Relationships with inverses (all should have them) only require one side to be set for the link to be established.
For example, if you set a Person's department property to a Department object, the inverse Department.people property would now also have this Person object contained inside.

Resources