I use Xcode 8 CoreData with auto generated Base classes.
When I try
let fetchRequest: NSFetchRequest<Event> = Event.fetchRequest()
fetchRequest variable correctly gets type of NSFetchRequest<Event>
When I try
let fetchRequest = Event.fetchRequest()
Xcode tells that fetchRequest has undefined type, as I understand Swift must determine type automatically by making assignment
Here is the auto generated class extension generated by Xcode
extension Event {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Event> {
return NSFetchRequest<Event>(entityName: "Event");
}
#NSManaged public var ffff: String?
#NSManaged public var timestamp: NSDate?
}
As an example this code works correctly (logic is the same)
struct MyStruct<T> {
let myVar: T
}
class MyClass {
}
extension MyClass {
class func test() -> MyStruct<Int> {
return MyStruct<Int>(myVar: 5)
}
}
let b = MyClass.test()
let b has a correct type of MyStruct<Int>
CoreDate automatically generated
#objc(Event)
public class Event: NSManagedObject {
}
extension Event {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Event> {
return NSFetchRequest<Event>(entityName: "Event");
}
#NSManaged public var timestamp: NSDate?
}
NSManagedObject protocol is in Objective-C and it has class method
+ (NSFetchRequest*)fetchRequest
When i try to use let fetchRequest = Event.fetchRequest() it thinks that i call Objective-C + (NSFetchRequest*)fetchRequest and it has no generics
Why doesn't it use an overridden #nonobjc public class func fetchRequest() -> NSFetchRequest<Event> from an Event class extension?
Related
I need to save a class in realm, this class contains a generic type as the following:-
#objcMembers
class ClassA<T: Object & Codable>: Object, Codable {
dynamic var key: String?
dynamic var type: T?
override class func primaryKey() -> String {
return "key"
}
}
#objcMembers
class ClassB: Object, Codable {
}
let object: ClassA<ClassB>
realm.add(object, update: true)
But this code unfortunately save only ClassA.key in realm and ignors ClassA.type.
I have googled about this issue but without any result unfortunately. It seems that nobody uses a generic type inside a realm class.
Finally I’ve reached the proper solution. I have used the advantage of the generic realm list type, then I have modified the “type” variable to be a list of generic instead of single generic object. It is now working fine with me. Now I can easily use a generic type inside a class and save this class to realm without any problem.
#objcMembers
class ClassA<T: Object & Codable>: Object, Codable {
dynamic var key: String?
var type: List<T>()
override class func primaryKey() -> String {
return "key"
}
}
#objcMembers
class ClassB: Object, Codable {
}
let object: ClassA<ClassB>
realm.add(object, update: true)
#objcMembers only exposes compatible members to Objective-C. Swift generics do not get included with this as they are not compatible with Objective-C. Realm works off of exposing the members to Objective-C.
Possible solution:
#objcMembers
class ClassA<T: Object & Codable>: Object, Codable {
dynamic var key: String?
var type: T?
override class func primaryKey() -> String {
return "key"
}
}
#objcMembers
class ClassB: Object, Codable {
}
class ClassABComposite: ClassA<ClassB> {
// T can be realized as ClassB here
override var type: ClassB? {
set {
backingType = newValue
}
get {
return backingType
}
}
// This will work because it's not generic
#objc dynamic var backingType: ClassB?
}
I' ve a Size class generated from Xcode for my size model, here it is:
#objc(Size)
public class Size: NSManagedObject {
override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
}
extension Size {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Size> {
return NSFetchRequest<Size>(entityName: "Size")
}
#NSManaged public var id: Int16
#NSManaged public var name: String?
var quantity: Int
func setQuantity() {}
}
Quantity var and setQuantity method are not accessible from my ViewController though I put public before their declaration, some suggestion?
You will be getting the compiler error: Extensions must not contain stored properties
You should update it and put the variable to your class instead of to extension.
public class Size: NSManagedObject {
var quantity: Int
// Same for other variables.
// Your existing code goes here.
}
Try and share your results.
Is this good or bad code for MVVM pattern with Core Data. For example i have Category+CoreDataClass.swift and CategoryDataProperties.swift; thats my
public class Account: NSManagedObject {
//what should i write here; any clue
}
extension Account {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Category> {
return NSFetchRequest< Category >(entityName: "Category")
}
#NSManaged public var categoryId: Int64
#NSManaged public var categoryTitle: String
}
Model = Category
class CategoryViewModel{
var category:Category() //
var categories: [Category]()
func AddCategory(category) {
//pass category to CoreData save method in CoreDataService.swift
}
}
and ViewModel is used in Single Screen with two textField named categoryId,categoryName(UIView)
class CategoryVC: UITableViewController {
var vm: CategoryViewModel!
}
I get this error (fatal error: NSArray element failed to match the Swift Array Element type) whenever I delete items from my collection view, close the app, relaunch the app, and then go back to the project. I've found similar solutions that re-ordered the initializers but that doesn't seem to wok for me.
**AppDelegate.swift**
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// register PFObject subclasses
Task.registerSubclass()
Task.initialize()
Project.registerSubclass()
Project.initialize()
User.registerSubclass()
User.initialize()
**Project.swift**
import UIKit
import Parse
public class Project: PFObject, PFSubclassing
{
// MARK: - Public API
#NSManaged public var projectTitle: String!
#NSManaged public var owner: User!
#NSManaged public var tasks: [Task]
// MARK - Required PFSubclassing Parse setup
override public class func initialize()
{
struct Static {
static var onceToken: dispatch_once_t = 0
}
dispatch_once(&Static.onceToken) {
Task.initialize()
self.registerSubclass()
}
}
public static func parseClassName() -> String {
return "Project"
}
// MARK: Conveinience Init
init(projectTitle: String, owner: User, tasks: [Task])
{
super.init()
self.projectTitle = projectTitle
self.owner = owner
self.tasks = tasks
}
override init() { super.init() }
}
**Task.swift**
import UIKit
import Parse
public class Task: PFObject, PFSubclassing
{
// MARK: - Public API
#NSManaged public var title: String
#NSManaged public var isChecked: Bool
#NSManaged public var projectOwner: Project
#NSManaged public var category: String
#NSManaged public var isImportant: Bool
// MARK - Required PFSubclassing Parse setup
override public class func initialize()
{
struct Static {
static var onceToken: dispatch_once_t = 0
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
public static func parseClassName() -> String {
return "Task"
}
// MARK: Conveinience Init
// Create new project
init(title: String, projectOwner: Project, isChecked: Bool, category: String, isImportant: Bool)
{
super.init()
self.title = title
self.projectOwner = projectOwner
self.isChecked = isChecked
self.category = category
self.isImportant = isImportant
}
override init() { super.init() }
}
The error specifically happens when I try to use the tasks array. I load it into a UICollectionView.
Here's possibly relevant code where I grab the tasks from Parse:
// Fetch data from Parse
func fetchProject() {
let isCurrentTab = navigationController?.tabBarItem.title
let tasksQuery = PFQuery(className: Task.parseClassName())
tasksQuery.whereKey("category", equalTo: isCurrentTab!)
tasksQuery.whereKey("projectOwner", equalTo: project)
tasksQuery.findObjectsInBackgroundWithBlock { (tasks, error) -> Void in
if error == nil {
self.project.tasks.removeAll()
print(tasks)
for task in tasks! {
print(task)
let task = task as! Task
self.project.tasks.append(task)
}
self.collectionView?.reloadData()
} else {
print("\(error?.localizedDescription)")
}
}
}
I'm using Xcode 7.1 and Parse v1.8.5
Any help would be much appreciated!
I ended up using pointers to access my tasks array instead of having it nested in the projects array. That way, I could make all of the calls directly to that array and not lose anything by trying to go down to the nested level.
It seems a class, which uses generics in swift, sometimes cannot properly determine object type.
Consider the following model structure:
class BaseModel: NSObject, Equatable, Printable {
var id: String = ""
override var description: String {
return "id: \(id)"
}
override func isEqual(object: AnyObject?) -> Bool {
if let object = object as? BaseModel {
return object.id == id
}
else {
return super.isEqual(object)
}
}
}
class Image: BaseModel {
var image: UIImage!
}
I also have parsers, which should parse/serialize objects:
class AbstractParser<T: BaseModel where T: Equatable>: NSObject {
func convertFromParseObject(object: NSObject) -> T {
var entity = T()
......
return updateEntityWithParseObject(object, entity: entity)
}
func updateEntityWithParseObject(object: NSObject, entity: T) -> T {
fatalError("This method must be overridden")
}
}
class ImageParser<T: Image>: AbstractParser<Image> {
override func updateEntityWithParseObject(object: NSObject, entity: Image) -> Image {
println("\(entity)")
println("\(entity.id)")
// The line below outputs BaseModel, shouldn't it be Image instead?
println("\(NSStringFromClass(entity.classForCoder))")
// EXC_BAD_ACCESS here:
println("\(entity.image)")
return entity
}
}
The app crashes when I try to access entity.image.
For some reasons Swift thinks that entity object is BaseModel, not Image.
Playground file: https://drive.google.com/file/d/0B6agzpK_lR6JQUlhMFoxaGw1akU/view?usp=sharing