Storing Array of Struct in core Data [duplicate] - ios

I´m trying to save an array of structs into coredata. I did a lot of research, but i cannot find the solution.
Here´s what i´ve got:
import Cocoa
import CoreData
class ViewController: NSViewController {
struct StudentsStruct {
let firstName: String
let lastName: String
let age: Int
}
let Studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]
override func viewDidLoad() {
super.viewDidLoad()
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
for items in Studentsdata {
student.firstName = StudentsStruct.firstName
student.lastName = StudentsStruct.lastName
student.age = StudentsStruct.age
}
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
print (student)
}
}
The DatabaseController is solution i´ve got from this tutorial:
https://www.youtube.com/watch?v=da6W7wDh0Dw
It´s not so important, it´s just making the "getContext" function.
Whats important, in teh commandline "student.firstName = StudentsStruct.firstName" i´m getting the error "instance member "firstName" cannot be used on type ViewController.StudentStruct.
After trying and trying, i´m running out of ideas how to get the array of structs into coredata.
This is the DatabaseController file:
import Foundation
import CoreData
class DatabaseController {
private init() {
}
class func getContext() -> NSManagedObjectContext {
return DatabaseController.persistentContainer.viewContext
}
// MARK: - Core Data stack
static var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "StudentCoreFile")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
fatalError("Unresolved error \(error)")
}
})
return container
}()
class func saveContext () {
let context = DatabaseController.persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
for any help thanks in advance!
Ok, you are right, i forgot to execute the fetchrequest. Here´s my current code:
import Cocoa
import CoreData
class ViewController: NSViewController {
struct StudentsStruct {
let firstName: String
let lastName: String
let age: Int
}
let Studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]
override func viewDidLoad() {
super.viewDidLoad()
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
for item in Studentsdata {
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
student.firstName = item.firstName
student.lastName = item.lastName
student.age = Int16(item.age)
}
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Student] {
print(student)
}
} catch {
print ("error")
}
}
}
It´s running without errors. Now i´m getting 32 search results. Every entry is: age = 0; firstName = nil; lastName = nil;
For comparison, this code, without the loop is working:
import Cocoa
import CoreData
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
student.firstName = "henry"
student.lastName = "miller"
student.age = 22
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Student] {
print(student)
}
} catch {
print ("error")
}
}
}

You need to access the item in your for loop also you are currently accessing the same object Student object in for loop instead of that you need to create a new Student in every iteration of for loop.
for item in Studentsdata {
//Create new student in for loop
let student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
//To get firstName, lastName and age access the item
student.firstName = item.firstName
student.lastName = item.lastName
student.age = item.age
}
//Save context now
DatabaseController.saveContext()

In case someone is interested, I found the solution:
You first have to set up the struct in the CoredataEntity Class like that:
import Foundation
import CoreData
struct StudentsStruct {
let firstName: String
let lastName: String
let age: Int
}
#objc(Student)
public class Student: NSManagedObject {
#NSManaged public var firstName: String?
#NSManaged public var lastName: String?
#NSManaged public var age: Int16
var allAtributes : StudentsStruct {
get {
return StudentsStruct(firstName: self.firstName!, lastName: self.lastName!, age: Int(self.age))
}
set {
self.firstName = newValue.firstName
self.lastName = newValue.lastName
self.age = Int16(newValue.age)
}
}
}
Then use the same struct to paste the data:
import Cocoa
import CoreData
class ViewController: NSViewController {
let studentsdata: [StudentsStruct] = [StudentsStruct(firstName: "Albert", lastName: "Miller", age: 24), StudentsStruct(firstName: "Susan", lastName: "Gordon", age: 24), StudentsStruct(firstName: "Henry", lastName: "Colbert", age: 24)]
override func viewDidLoad() {
super.viewDidLoad()
for items in studentsdata {
let student: Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
student.allAtributes = items
}
DatabaseController.saveContext()
let fetchRequest: NSFetchRequest<Student> = Student.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
print("number of results: \(searchResults.count)")
for result in searchResults as [Student] {
print("student: \(firstName), \(lastName), \(age)" )
}
} catch {
print ("error: \(error)")
}
}
}
Thats it.

Perhaps this is lazy. You could also just encode your array as a json object and then create a field on your NSManagedObject for it as a transformable. When you want to retrieve you'd just decode and downcast to the proper type. That's what I did in one of my projects; worked fine.

Related

Correct way of creating reference to object in Realm

I create a fitness app and I use Realm as local database. During first launch I want to replace default realm with realm file which contains initial data (names of exercises, equipment, muscles engaged etc.). This initial data won't change in future. I wonder if exists some way which can help me to create reference in main class to another smaller classes. I need this to make filtering and getting data easier.
It's my main realm class
class Exercise: Object {
#Persisted var exerciseID: Int = 0
#Persisted var name: String = ""
#Persisted var category: Int
#Persisted var equipment: String
#Persisted var instruction: String
#Persisted var muscle: String
#Persisted var gif: String?
#Persisted var image: String? = nil
convenience init(name: String, category: Int, equipment: String, instruction: String, muscle: String, gif: String?, image: String?) {
self.init()
self.name = name
self.category = category
self.equipment = equipment
self.instruction = instruction
self.muscle = muscle
self.gif = gif
self.image = image
}
override static func primaryKey() -> String? {
return "exerciseID"
}
}
When I want to get all exercises and assigned equipment and muscles it is really a lot of code to retrieve this data especially when string contains few references to object.
var exercises = [Exercise]()
var equipments = [Equipment]()
func getAllExercises() {
let data = RealmService.shared.realm.objects(Exercise.self)
exercises = data.compactMap({$0})
let equipment = exercises.compactMap({$0.equipment})
for eq in exercises.compactMap({$0.equipment}) {
let numberOfEquipment = eq.components(separatedBy: ",")
for number in numberOfEquipment {
guard let intNumber = Int(number) else { return }
guard let finalEquipment = RealmService.shared.realm.object(ofType: Equipment.self, forPrimaryKey: intNumber) else { return }
equipments.append(finalEquipment)
}
}
Maybe the better option is to just insert values instead of object references?
You need to set up one-to-many relationships to take advantage of quicker queries and lazy loading.
I've simplified the models, but the magic is in the equipmentObjects property:
class Exercise: Object {
#Persisted(primaryKey: true) var exerciseID = 0
#Persisted var name: String = ""
#Persisted var equipment: String
#Persisted var equipmentObjects: List<Equipment>
convenience init(exerciseID: Int, name: String, equipment: String) {
self.init()
self.exerciseID = exerciseID
self.name = name
self.equipment = equipment
}
}
class Equipment: Object {
#Persisted(primaryKey: true) var equipmentID = 0
#Persisted var equipment: String = ""
convenience init(equipmentID: Int, equipment: String) {
self.init()
self.equipmentID = equipmentID
self.equipment = equipment
}
}
You can go ahead and initialize realm with your csv file. But when the app begins you would want to go ahead and establish the relationships between Exercise, Equipment, and Muscles. You should only do this once.
Here I've created a small utility to link the realm objects. Notice how it uses UserDefaults to check and see if relationships were already built. It is also building the relationships on a specified queue. You would want to pass in a background queue rather than the main queue so the UI doesn't lock up.
struct RealmRelationshipBuilder {
let configuration: Realm.Configuration
let userDefaults: UserDefaults
let queue: DispatchQueue
func buildRelationshipsIfNeeded(completion: #escaping() -> Void) {
guard userDefaults.didBuildRealmRelationships == false else { return completion() }
queue.async {
autoreleasepool {
defer { completion() }
do {
let realm = try Realm(configuration: configuration)
try realm.write {
realm.objects(Exercise.self).forEach { exercise in
let equipment = exercise
.equipment
.components(separatedBy: ",")
.compactMap(Int.init)
.compactMap { realm.object(ofType: Equipment.self, forPrimaryKey: $0) }
exercise.equipmentObjects.append(objectsIn: equipment)
}
}
} catch {
print("RealmRelationshipBuilder error: \(error)")
}
userDefaults.didBuildRealmRelationships = true
}
}
}
}
extension UserDefaults {
enum Key {
static let didBuildRealmRelationships = "didBuildRealmRelationshipsKey"
}
var didBuildRealmRelationships: Bool {
get { bool(forKey: Key.didBuildRealmRelationships) }
set { set(newValue, forKey: Key.didBuildRealmRelationships) }
}
}
Then to test the builder here is a small test case. But in reality you would probably want to show the user an status indicator while the relationships are being built in the background.
enum InitialData {
static let exercises: [Exercise] = {
[
Exercise(exerciseID: 1, name: "Bench press", equipment: "1,3,5"),
Exercise(exerciseID: 2, name: "Butterfly", equipment: "6"),
]
}()
static let equipment: [Equipment] = {
[
Equipment(equipmentID: 1, equipment: "Barbell"),
Equipment(equipmentID: 2, equipment: "Bench"),
Equipment(equipmentID: 3, equipment: "Bodyweight"),
Equipment(equipmentID: 4, equipment: "Cable"),
Equipment(equipmentID: 5, equipment: "Not sure"),
Equipment(equipmentID: 6, equipment: "Unknown"),
]
}()
}
class RealmExerciseTests: XCTestCase {
let realmConfiguration = Realm.Configuration.defaultConfiguration
override func setUpWithError() throws {
let realm = try Realm(configuration: realmConfiguration)
try realm.write {
realm.deleteAll()
realm.add(InitialData.exercises)
realm.add(InitialData.equipment)
}
}
func testInitialize() throws {
let relationshipBuilder = RealmRelationshipBuilder(
configuration: realmConfiguration,
userDefaults: .init(suiteName: UUID().uuidString) ?? .standard,
queue: DispatchQueue(label: "realm.init.background")
)
let expectation = expectation(description: "realm.init")
relationshipBuilder.buildRelationshipsIfNeeded {
expectation.fulfill()
}
wait(for: [expectation], timeout: 2.0)
let realm = try Realm(configuration: realmConfiguration)
realm.refresh()
guard let exercise1 = realm.object(ofType: Exercise.self, forPrimaryKey: 1) else {
return XCTFail("Missing exercise with primary key 1")
}
guard let exercise2 = realm.object(ofType: Exercise.self, forPrimaryKey: 2) else {
return XCTFail("Missing exercise with primary key 2")
}
XCTAssertEqual(exercise1.equipmentObjects.count, 3)
XCTAssertEqual(exercise2.equipmentObjects.count, 1)
}
}

Array is updating before variables are updated in Swift

I'm trying to get list of toys from Firestore and put it into array
But when I call function, it returns empty array, and just after returning it prints Toy object, so order is broken.
I thought that closures would help me, but I think I don't know how to use them, and examples from Google don't help me
Here is my code (I use SwiftUI so I created swift file with variable)
let db = Firestore.firestore()
class DataLoade {
func loadFirebase(completionHandler: #escaping (_ toys: [Toy]) -> ()){
var toysar: [Toy] = []
let toysRef = db.collection("Toys")
toysRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
var name: String = document.get("name") as! String
var id: Int = document.get("id") as! Int
var description: String = document.get("description") as! String
var imageName: String = document.get("imageName") as! String
var price: String = document.get("price") as! String
var category: String = document.get("category") as! String
var timeToy = Toy(id: id, name: name, imageName: imageName, category: category, description: description, price: price)
toysar.append(timeToy)
}
}
}
completionHandler(toysar)
// print(toysar)
}
}
that's what it prints out:
[] // it prints empty array, but it is in the end of the code
Toy(id: 1001, name: "Pikachu", imageName: "pikachu-plush", category: "lol", description: "kek", price: "350₽") // and now it prints Toy object, however it is in the start of the code
Ok, so I tried to make completion handler for my function, like in "duplicated" answer, but that doesn't work: array is returning before completion handler works
ContentView.swift
func updateArray() -> [Toy]{
dl.loadFirebase() { toys in
ll = toys
}
print("lol \(datas)") // prints «lol []»
return ll
}
You can wait for an asynchronous task using a DispatchGroup. But the trick is NOT to associate asynchronous tasks with return statements. Instead, use closures to do an action after the task is done.
Disclaimer: I wrote this on SO, I apologize in advance for syntax issues.
let toyData = loadFirebase( { (toys) in
print(toys)
//Do something with toys when done
//You could add another completionHandler incase it fails.
//So 1 for pass and 1 for fail and maybe another for cancel. W/e u want
} )
let db = Firestore.firestore()
func loadFirebase(completionHandler:#escaping ((toys: [Toy]?) -> Void)) {
//Create Group
let downloadGroup = DispatchGroup()
var toysar: [Toy] = []
let toysRef = db.collection("Toys")
//If you had multiple items and wanted to wait for each, just do an enter on each.
downloadGroup.enter()
toysRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
var name: String = document.get("name") as! String
var id: Int = document.get("id") as! Int
var description: String = document.get("description") as! String
var imageName: String = document.get("imageName") as! String
var price: String = document.get("price") as! String
var category: String = document.get("category") as! String
var timeToy = Toy(id: id, name: name, imageName: imageName, category: category, description: description, price: price)
toysar.append(timeToy)
print(timeToy)
}
//We aren't done until AFTER the for loop, i.e., each item is grabbed.
downloadGroup.leave()
}
}
//Once the queue is empty, we notify the queue we are done
downloadGroup.notify(queue: DispatchQueue.main) {
completionHandler(toys)
}
}
import SwiftUI
var dl = DataLoade()
var ll: [Toy] = []
let semaphore = DispatchSemaphore(value: 1)
struct ContentView: View {
var items: [Toy]
var body: some View {
NavigationView{
ScrollView(){
VStack(alignment: .leading){
ToyRow(category: "Наш выбор", toys: items)
Spacer()
ToyRow(category: "Акции", toys: items)
}
}.navigationBarTitle(Text("Игрушки г.Остров"))}
}
}
func upe(completionHandler:#escaping ((toys: [Toy]?){
dl.loadFirebase(completionHandler: { toy in
ll.append(contentsOf: toy!)
completionHandler(ll)
} )
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
upe(completionHandler: { (toys) in
DispatchQueue.main.async {
ContentView(items: toys)
}
})
}
}

Swift Realm migration create reference from old type to new one

Initially I had the following classes:
#objcMembers public class NormalObjectRealm: Object {
// Shared
dynamic public var id: String?
dynamic public var title: String?
dynamic public var subTitle: String?
dynamic public var imageInfo: ImageInfoRealm?
dynamic public var descriptionString: String?
public var categories = List<String>()
public var count = RealmOptional<Int>()
public var episodes = List<String>()
public static let realmPrimaryKey: String = "id"
public override class func primaryKey() -> String? {
return NormalObjectRealm.realmPrimaryKey
}
}
#objcMembers public class ImageInfoRealm: Object {
dynamic public var id: String?
dynamic public var url: String?
public static let realmPrimaryKey: String = "id"
public override class func primaryKey() -> String? {
return ImageInfoRealm.realmPrimaryKey
}
}
but now NormalObjectRealm is kind of incorporated into a new class like so:
#objcMembers public class MediaObjectRealm: Object {
// Shared
dynamic public var id: String?
dynamic public var title: String?
dynamic public var subTitle: String?
dynamic public var imageInfo: ImageInfoRealm?
dynamic public var descriptionString: String?
public var categories = List<String>()
dynamic public var type: String?
// NormalObjectRealm
public var episodeCount = RealmOptional<Int>()
public var episodes = List<String>()
// OtherObjectRealm
dynamic public var urlOne: String?
dynamic public var urlTwo: String?
dynamic public var urlThree: String?
public var isExplicit = RealmOptional<Bool>()
public static let realmPrimaryKey: String = "id"
public override class func primaryKey() -> String? {
return MediaObjectRealm.realmPrimaryKey
}
}
I'm currently trying to write the migration for the transition here where the idea basically is to transfer most of the fields over from NormalObjectRealm to MediaObjectRealm.
This is what my migration-block currently looks like
Realm.Configuration(schemaVersion: schemaVersion, migrationBlock: { migration, oldSchemaVersion in
if oldSchemaVersion < temp {
print("RealmMigration: Applying migration from \(oldSchemaVersion) to \(temp)")
migration.enumerateObjects(ofType: "NormalObjectRealm") { oldObject, newObject in
guard let oldObject = oldObject else {
return
}
guard let id = oldObject["id"] as? String else {
return
}
guard let title = oldObject["title"] as? String else {
return
}
guard let subTitle = oldObject["subTitle"] as? String else {
return
}
guard let imgInfo = oldObject["imageInfo"] else {
return
}
guard let count = oldObject["count"] as? RealmOptional<Int>? else {
return
}
guard let descriptionString = oldObject["descriptionString"] as? String? else {
return
}
let item = migration.create("MediaObjectRealm")
item["id"] = id
item["title"] = title
item["subTitle"] = subTitle
item["descriptionString"] = descriptionString
item["type"] = "myType"
item["episodeCount"] = episodeCount // Doesn't work either...
migration.enumerateObjects(ofType: "ImageInfoRealm") { oldImg, newImg in
guard let oldImg = oldImg else {
return
}
let inf = oldObject.value(forKey: "imageInfo")
print(inf)
let t = migration.create("ImageInfoRealm", value: inf)
print("doing it")
// print(t)
item.setValue(t, forKey: "imageInfo")
}
}
}
})
id, title, subTitle etc. (String? and Date? variables) are set fine and appear inside the newly created MediaObjectRealm DB-Entries. However imageInfo of type ImageInfoRealm does not... setting it directly like so: item.setValue(oldObject.value(forKey: "imageInfo"), forKey: "imageInfo") (or item["imageInfo"] = oldObject.value(forKey: "imageInfo")) results in realm crashing and telling me that this object is from another realm and I have to copy it over.
'Object is already managed by another Realm. Use create instead to
copy it into this Realm.'
Creating it like in the code above results in not even having any items of type MediaObjectRealm at all i.e. loosing all the data (as NormalObjectRealm is also not present anymore).
Am I missing something? What I basically want is to to take the link/reference from the NormalObjectRealm and copy it to the new MediaObjectRealm.
After long testing and trying different possibilities I managed to migrate the data.
Here is what I did to accomplish this.
I used this as a base:
class RealmMigrationObject {
let migration: () -> ()
init(migration: #escaping () -> ()) {
self.migration = migration
}
}
and derived classes from that. Something like:
class MigrationObjectToThree: RealmMigrationObject {
init() {
super.init(migration: MigrationObjectToThree.migration)
}
private static func migration() {
print("Migration to three | migration")
var imageInfos: [ImageInfo] = []
let config = Realm.Configuration(schemaVersion: 3, migrationBlock: { migration, oldSchemaVersion in
print("Migration to three | migrationBlock")
print("RealmMigration: Applying migration from \(oldSchemaVersion) to 3")
migration.deleteData(forType: "ExploreSectionObjectRealm")
migration.enumerateObjects(ofType: "ImageInfoRealm") { oldInfo, newObject in
guard let oldInfo = oldInfo else {
return
}
guard let id = oldInfo["id"] as? String,
let url = oldInfo["url"] as? String,
let url500 = oldInfo["url500"] as? String,
let url400 = oldInfo["url400"] as? String,
let url300 = oldInfo["url300"] as? String,
let url200 = oldInfo["url200"] as? String,
let url100 = oldInfo["url100"] as? String,
let colorString = oldInfo["color"] as? String,
let color = UIColor(hexString: colorString) else {
return
}
imageInfos.append(ImageInfo(id: id,
url: url,
url500: url500,
url400: url400,
url300: url300,
url200: url200,
url100: url100,
color: color))
}
})
Realm.Configuration.defaultConfiguration = config
do {
let realm = try Realm(configuration: config)
print("Realm is located at: \(realm.configuration.fileURL?.description ?? "")")
print(realm.configuration.fileURL?.description ?? "") // Printing here on purpose as it's easier to copy
} catch {
print("Realm Error: \(error), trying to rebuild realm from scratch")
let deleteMigrationConfig = Realm.Configuration(schemaVersion: RealmHelper.schemaVersion,
deleteRealmIfMigrationNeeded: true)
do {
_ = try Realm(configuration: deleteMigrationConfig)
} catch {
print("Failed to instantiate: \(error.localizedDescription)")
}
}
RealmHelper.removeRealmFiles()
Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 3)
imageInfos.forEach({ $0.save() })
}
}
From that I just created all migration for the difference between the current schema version and target schema version on looped over all migrations simply executing the migration function of that given object.

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)

Parse PFSubclassing in Swift of Object type

I'm pretty new to iOS/Swift/Parse and I'm trying to build a model of a class using PFSubclassing.
The data I'm trying to represent should look something like this
{
text: ""
location : {
name: "",
longitude: "",
latitude: ""
}
}
So fare the model I'm have is
class LocationModel {
var name: String?
var longitude: Float?
var latitude: Float?
}
class PostModel: PFObject, PFSubclassing {
class func parseClassName() -> String! {
return "Post"
}
#NSManaged var text: String?
var location: LocationModel?
}
The test property is being saved successfully but I'm unable to get the location properties to save.
The code I'm using to save a record to parse is
var test = PostModel()
test.location?.name = "ESB"
test.location?.latitude = 1
test.location?.longitude = 1
test.text = "This is a test post to see if this works!"
test.saveEventually { (success: Bool, error: NSError!) -> Void in
println(error)
println(success)
}
I did a lot of digging online but I'm unable to find a solution on how to represent an Object datatype in Swift using Parse PFSubclassing
Any help would be greatly appreciated.
Thank you
Here's my solution:
I will create a Hero object for example.
class Hero: PFObject, PFSubclassing {
#NSManaged var strengthPoint: Double
#NSManaged var name: String
static func parseClassName() -> String {
return "Hero"
}
init(strengthPoint: Double, name: String) {
super.init()
self.strengthPoint = strengthPoint
self.name = name
}
init(pfObject: PFObject) {
super.init()
self.strengthPoint = pfObject.object(forKey: "strengthPoint") as! Double
self.name = pfObject.object(forKey: "name") as! String
}
override init() {
super.init()
}
override class func query() -> PFQuery<PFObject>? {
let query = PFQuery(className: self.parseClassName())
query.order(byDescending: "createdAt")
query.cachePolicy = .networkOnly
return query
}
}
Now, after defining your model, you can use these methods to store and retrieve
Create your object in server
func createHero() {
let hero = Hero(strengthPoint: 2.5, name: "Superman")
hero.saveInBackground { (isSuccessful, error) in
print(error?.localizedDescription ?? "Success")
}
}
Retrieve object from server
func retrieveHero() {
let query = Hero.query()
query?.getFirstObjectInBackground(block: { (object, error) in
if error != nil {
print(error?.localizedDescription ?? "Something's wrong here")
} else if let object = object {
let hero = Hero(pfObject: object)
print(hero.strengthPoint) // 2.5
print(hero.name) // Superman
}
})
}
I have seen several different methods for PFSubclassing in Swift 1.2, but the following works best for me:
To begin with, make sure that you have the following in your Objective-C Bridging Header:
#import <Parse/PFObject+Subclass.h>
Here is a very basic example of subclassing PFObject:
import Foundation
import Parse
class Car: PFObject, PFSubclassing {
override class func initialize() {
self.registerSubclass()
}
static func parseClassName() -> String {
return "Car"
}
#NSManaged var model: String
#NSManaged var color: String
#NSManaged var yearManufactured: Int
}
So in your case, this would be:
class PostModel: PFObject, PFSubclassing {
override class func initialize() {
self.registerSubclass()
}
static func parseClassName() -> String {
return "Post"
}
#NSManaged var text: String?
}
Concerning your LocationModel...I'm a bit confused as to what exactly you are trying to accomplish with that. I hope this helps.

Resources