Using the Parse.com Object Column - swift

I cannot find documentation on the Parse.com table column Object data type. I assume it is an object in the context of software development. However, what is the syntax to use to enter an object into the object column? I would be interested to know both the programmatic steps to take (not too concerned about which language, more concerned about actions to take to save to the object column), but I would be even more interested to know how to enter an object into the table from the Parse.com website. Can we do this from the Data section of the "Core" tab, in the Parse.com Dev webpage for the app?
I did this little test in Swift to try and save an object with a property of type object (myCar) to the table from code (I have a Parse.com table with class name Test, which has an object column called myCar). It is causing an error (I'm new to iOS so cannot find out much about the error):
Car class
class Car {
var doors = 4
func addDoor() {
doors++
}
}
client code:
override func viewDidLoad() {
var testMan = PFObject(className:"Test")
var car = Car()
testMan["myCar"] = car //////////////// error here
testMan.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
println("Success")
} else {
// There was a problem, check error.description
println("Failure")
}
}
}

well, u're mixing swift objects with parse objects .. if you want a Car object linked to your Test class you must create it in parse database (+ add class -> car -> and then create properties in it). When you have created parse class Car u can use it in swift code as let car = PFObjecT(className: "Car") and later on assign that class to test object with testMan["myCar"] = car

Related

Swift Core Data - storing an array of custom types

I am trying to create a data model which mirrors a view model that I use to handle an API call, the idea being that I will be able to store all the necessary data in core data and then access it when the user is offline, effectively giving the app offline functionality.
However, there is one entity which I need to store which is an array of a custom class that I have in the app:
[OrderSheet]
This is a struct defined as follows:
struct OrderSheet {
let order: SheetClass // codable class
let sheet: Sheet // codable struct
init(fuelOrder: SheetClass, sheet: Sheet) {
self.order = order
self.sheet = sheet
}
}
How can I create an entity that would be capable of storing the above?
One simple way would be to have an entity that holds only one Data field (Binary Data in xcdatamodel settings), which would be the orderSheet itself.
Before going with this solution, I'd like to mention that, one down side of this approach is; if later in the future, any of the models inside OrderSheet changes, you won't be able to retrieve already stored objects as conversion will fail. One way of overcoming this issue would be declaring everything inside OrderSheet and sub models as Optional. But if it is not so crucial, meaning, if not being able to read old models on user's device after an app update is okay, (maybe they will be replaced with new networking call) then you can go with not marking properties as optional either.
Lets imagine you create an entity named OrderSheetManaged with one field as I mentioned like following:
import Foundation
import CoreData
#objc(Entity)
public class OrderSheetManaged: NSManagedObject {
}
extension OrderSheetManaged {
#nonobjc public class func fetchRequest() -> NSFetchRequest<OrderSheetManaged> {
return NSFetchRequest<OrderSheetManaged>(entityName: "OrderSheetManaged")
}
#NSManaged public var orderSheet: Data?
}
I will write some code for NSManagedObjectContext, which is not directly related to your question, you should make research on how to initialise a core data stack and a managed context from it if you are not familiar with that since it is crucial.
I also do some force unwrapping for simplicity, make sure to not force unwrap where not needed in production code.
Now whenever you have an actual OrderSheet object (it is orderSheet in my example below), that was parsed before, you are going to convert it to Data and persist it with new Core Data model as following:
// unrelated to question, it should already be initialised from core data stack, I just init with
// concurrency type to make compiler happy, dont do this before further research.
let yourManagedContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
let entityDescription = NSEntityDescription.entity(forEntityName: "OrderSheetManaged",
in: yourManagedContext)
let dataForCoreData = try! JSONEncoder().encode(orderSheet)
let managedOrderSheet = NSManagedObject(entity: entityDescription!, insertInto: yourManagedContext)
managedOrderSheet.setValue(dataForCoreData, forKey: "orderSheet")
Now we have persisted your object as Data inside a wrapper core data model (OrderSheetManaged)
Let's see now how we can fetch these models from our core data and convert it back to OrderSheet model:
// when you fetch it
var orderSheets = [OrderSheet]()
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "OrderSheetManaged")
var coreDataObjects: [NSManagedObject]!
do {
coreDataObjects = try yourManagedContext.fetch(request) as? [NSManagedObject]
for coreDataObject in coreDataObjects {
if let orderSheetData = coreDataObject.value(forKey: "orderSheet") as? Data {
let orderSheet = try! JSONDecoder().decode(OrderSheet.self, from: orderSheetData)
orderSheets.append(orderSheet)
}
}
} catch {
error
}
Now you will have all your stored order sheets inside orderSheets array.
You can also write some utility methods to easily modify core data models by converting orderSheet data inside of them to OrderSheet first and then again converting it back to Data after modifying and then persisting again with setValue.

Enum for .mlmodels?

So I am currently working on a project that includes image recognition with CreateML, CoreML, and Vision. I am still trying datasets and improving the models with CreateML, but if I change from a model to another, I have to manually change a variable let model = example() that Xcode creates for me when I import the .mlmodel file into the project. So I wanted to do a tableView with the name of those models files so if I tap on one of them, it takes me to the "RecognitionVC", passing the name as a variable so I can instantiate the class with an enum from a rawValue(string) and then access its "model variable" which I cannot achieve.
This is what the enum would look like:
enum MLModels: String {
case example
case letters
case ab
case numbers
case asl
}
And this is what I would like to achieve:
func getModel() -> AnyClass {
switch self {
case .example:
return example()
...
}
}
var model: MLModel {
switch self {
case .example:
return example()
...
}
}
I am new to these machine learning built in frameworks, if someone could let me know what am I doing wrong or how can I achieve this I would be very grateful.I am including some screenshots. And in case you want to test anything I am including the link to apple's public models:
link
When you write example() it creates an instance of a wrapper class. This is not an MLModel. However, it does have the MLModel as a property, so you could write return example().model to do what you want.

How to set the value of lazy computed property via a closure in Swift?

So I've been stuck on this problem for a while, and can't find questions addressing my particular problem online.
I am trying to set the value in description, which is defined as a lazy computed property and utilizes a self-executing closure.
To get the book's description, I make an API call, passing in another handler to the API completion handler so that I can set the book's description inside the lazy computed property.
I know my below code is wrong, since I get the error:
Cannot convert value of type '()' to specified type 'String'
class Book : NSObject {
func getInfo(for name: String, handler: #escaping (_ string: String) -> String) {
let task = URLSession.shared.dataTask(with: "foo_book.com" + name) { (data, response, error) in
guard let data = data else {return}
descriptionStr = String(data: data, encoding: .utf8) ?? "No description found"
handler(descriptionStr)
}
}
lazy var description: String = {
getInfo(for: self.name) { str in
return str
}
}()
}
How can I set the value of description?
I've tried two methods. Using a while loop to wait for a boolean: inelegant and defeats the purpose of async. Using a temp variable inside description - doesn't work because getInfo returns before the API call can finish.
In case you wonder my use case: I want to display books as individual views in a table view, but I don't want to make api calls for each book when I open the tableview. Thus, I want to lazily make the API call. Since the descriptions should be invariant, I'm choosing to make it a lazy computed property since it will only be computed once.
Edit: For those who are wondering, my solution was as the comments mentioned below. My approach wasn't correct - instead of trying to asynchronously set a property, I made a method and fetched the description in the view controller.
Already the explanation in comments are enough for what's going wrong, I will just add on the solution to your use case.
I want to display books as individual views in a table view, but I
don't want to make api calls for each book when I open the tableview.
Thus, I want to lazily make the API call.
First of all, does making lazy here make sense. Whenever in future you will call description, you are keeping a reference for URLSession and you will do it for all the books. Looks like you will easily create a memory leak.
Second, task.resume() is required in getInfo method.
Third, your model(Book) should not make the request. Why? think, I have given one reason above. Async does mean parallel, all these network calls are in the queue, If you have many models too many networks calls in the event loop.
You can shift network call responsibility to service may be BookService and then have a method like this BookService.getInfo(_ by: name). You Book model should be a dumb class.
class Book {
let description: String
init(desc: String) {
self.description = desc
}
}
Now your controller/Interactor would take care of calling the service to get info. Do the lazy call here.
class BookTableViewController: ViewController {
init(bookService: BookService, book: [String]) {
}
# you can call when you want to show this book
func loadBook(_ name: String) -> Book {
BookService.getInfo(name).map { Book(desc: str) }
}
func tableView(UITableView, didSelectRowAt: IndexPath) {
let bookName = ....
# This is lazy loading
let book = loadBook(bookName)
showThisBook()
}
}
Here, you can do the lazy call for loadBook. Hope this helps.

Swift Realm getting stuck on re-adding an Object in a write block

I'm using Realm, the project is on version 1.0.0.
When I create a list of Realm Objects (with data obtained from a web API), then try to save them to the Realm using this utility function in a struct:
static func saveRealmObjects(objects: [Object]) {
defer {
// Never entered
}
for object in objects {
let realm = try! Realm()
do {
try realm.write {
print("TEST: 1: object: \(object)")
realm.add(object)
print("TEST: 2")
}
} catch {
// Never entered
}
}
}
(Please don't judge me on the exact structure, I've been toying around seeing if anything will work).
I can tell from liberal use of print statements (mostly removed above) that the function gets to TEST: 1 okay, but fails to make it to TEST: 2, for the very first Object in the list I pass to the function.
I should note this function does work the first time I use it with the data (say after wiping the simulator and launching the app afresh), but then if I recreate the Objects and try to save them again it gets stuck.
I assumed Realm would use the private key on the Objects and overwrite any if necessary. But it seems to just get stuck.
-
Then - after it's stuck - if I try and get another set of results from Realm (using a different Realm object) I get the following error:
libc++abi.dylib: terminating with uncaught exception of type realm::InvalidTransactionException: Cannot create asynchronous query while in a write transaction
FYI I'm creating a different Realm object using try! Realm()
-
For reference, here is the Object I'm trying to save:
import Foundation
import RealmSwift
class MyObject: Object {
// MARK: Realm Primary Key
dynamic var id: String = ""
override static func primaryKey() -> String? {
return "id"
}
// MARK: Stored Properties
dynamic var date: NSDate? = nil
dynamic var numA = 0
dynamic var numB = 0
dynamic var numC = 0
dynamic var numD = 0
dynamic var numE = 0
dynamic var numF = 0
dynamic var numG = 0
dynamic var numH = 0
// MARK: Computed Properties
var computedNumI: Int {
return numD + numE
}
var computedNumJ: Int {
return numF + numG
}
}
(The variable names have been changed.)
-
Hopefully I'm doing something obviously wrong - this is my first time using Realm after all.
If you have any ideas why it's sticking (perhaps it's a threading issue?), or want more info, please answer or comment. Thank you.
Being the clever clogs I am, I've literally just found the answer by reading the documentation:
https://realm.io/docs/swift/latest/#creating-and-updating-objects-with-primary-keys
The add to Realm line needed to look like this:
realm.add(object, update: true)
Where the update flag will update Objects already saved with that primary key.
-
Although it would have been nice if it either gave some sort of obvious warning or crash upon trying to add the same object, or didn't cause other queries and writes to Realm to crash.

Dynamically mirror properties to another class

I'm currently working on using Realm together with the Swift framework Oatmeal. Now, both of those require model classes (Oatmeal can do automatic serialisation). My structure for a given Oatmeal model and Realm model are as follows:
class OatmealModel: SerializableObject {
var id: Int = 0
var someOtherProperty: String?
var anotherUnknownProperty: SomeType?
var store: RealmModel?
}
class RealmModel: Object {
dynamic var id: Int = 0
dynamic var someOtherProperty: String?
dynamic var anotherUnknownProperty: SomeType?
override static func primaryKey() -> String? {
return "id"
}
}
As should be clear, they both hold the same properties, but the OatmealModel is always the one I interact with. Aside from this, the OatmealModel also implements a protocol to retrieve data:
protocol PersistentStore {
func save()
func findStore()
func all() -> [PersistentStore]
func filter(predicate: NSPredicate) -> [PersistentStore]
}
These methods allow us to fetch from the Realm database and then hydrate the oatmeal model, and get only oatmeal models back. And then calling save will write back to realm.
So essentially, what I want to do is:
When retrieving entities from the Realm DB and hydrating Oatmeal models, this should be done automatically. Currently, every oatmeal model has to set the all and filter (and save) methods separately just to replace the class name, and the properties currently have to be set manually. My plan is to somehow use reflection on both the oatmeal model and the realm model to see which properties have the same name and type, and then write them from one to the other.
Currently, I have no idea how to go about this, so any hints on how I could use reflection for this would be helpful. I assume somehow use the Mirror to extract the properties and then loop through them and see if one of the same name exists in the other?
Also, to achieve this in the end, since it'll be the models doing this to return instances of itself, is it somehow possible for a model to create an array of its own type, without explicitly writing the type? E.g [Self] (which doesn't work) rather than [OatmealModel]?
I'm fairly new to Swift, but am familiar with a few other languages and concepts, any help is greatly appreciated!