How to have columns in table that don't have to be specified in post request - vapor

I have a Vapor app where I want some values to be specified by the user in a POST request, and other values to be computed based on the user-specified values.
For example, suppose the user patches in some new values, and each time that happens the table should automatically update a column with the current time.
I was looking at trying to store the computed properties in the database, but when I modified the model to know about the computed properties, all my POST requests began expecting those to be specified.
What's the most idiomatic way to have columns in a table that don't have to be specified by the post requests?

If you are only looking to update a modified or created timestamp then there are two other ways. In your model, put:
static let createdAtKey: TimestampKey? = \.createdAt
static let updatedAtKey: TimestampKey? = \.updatedAt
var createdAt:Date?
var updatedAt:Date?
And let vapor do it for you, see here. Alternatively, you can make use of the methods willCreate, willUpdate, etc. as described in the docs here if you are updating fields that do not need the user's input.
extension User
{
func willUpdate(on connection: Database.Connection) throws -> Future<User>
{
modifiedCount += 1
return Future.map(on: connection) { self }
}
}
Finally, if you need a bit more flexibility than your own solution, consider using this in your controller:
struct EditUserForm:Content
{
let id:Int
let surname:String
let initials:String
}
func save(_ request:Request) throws -> Future<View>
{
return try request.content.decode(EditUserForm.self).flatMap
{
newUserData in
return try request.parameters.next(User.self).flatMap
{
originalUser in
// update fields as required, EditUserForm only has a subset
return originalUser.save(on:request).transform(to:try self.index(request))
}
}
}
You will need the usual route:
router.post(User.parameter, "save", use:userController.save)

I found that I need to make the computed fields be optional in the model, and then compute them in the route function before saving.
For example:
Making modified_date be optional in the model:
final class MyContentType: PostgreSQLModel {
var id: Int?
var name: String
var modified_date: Date?
}
Setting modified_date to the computed value:
func create(_ request: Request, content: MyContentType) throws -> Future< MyContentType > {
content.modified_date = Date()
return content.save(on: request)
}

Related

Is Realm smart when updating values or check for new values should be perfomed manually?

I wonder if I should create my own additional layer when updating Realm objects to avoid redundant database writing operations or is it done automatically on a lower level?
Let's take an example:
class SomeEntity: Object {
#Persisted(primaryKey: true) var id = 0
#Persisted var aaa: String?
#Persisted var bbb: Float?
#Persisted var ccc: Int?
}
when doing some batch update:
newDownloadedData.forEach { entry in
guard let id = entry["id"].int else {
return
}
try? localRealm.write {
let entity = existingLocalEntities.first { $0.id == id } ?? SomeEntity(id: id)
localRealm.add(entity, update: .modified) //this makes an 'upsertion' which is automatically an update or insert
if entity.aaa != entry["aaa"].string {
entity.aaa = movieInfo["aaa"].string
}
if entity.bbb != entry["bbb"].float {
entity.bbb = movieInfo["bbb"].float
}
if entity.ccc != entry["ccc"].int {
entity.ccc = movieInfo["ccc"].int
}
}
}
I wonder if these checks necessary or can I just go with:
entity.aaa = movieInfo["aaa"].string
entity.bbb = movieInfo["bbb"].float
entity.ccc = movieInfo["ccc"].int
and not worry that values will be updated and written even if downloaded values are the same as existing local ones?
Your observers will be notified if you update a property on a realm object with the same value. Realm does not care if you use a different value or not.
I'm not sure what your use case is, but it may be a pain in the butt to check every value manually.
You can do something like this though:
protocol UniqueUpdating { }
extension UniqueUpdating where Self: AnyObject {
#discardableResult
func update<Value: Equatable>(
_ keyPath: ReferenceWritableKeyPath<Self, Value>,
to value: Value
) -> Bool {
guard self[keyPath: keyPath] != value else { return false }
self[keyPath: keyPath] = value
return true
}
}
extension Object: UniqueUpdating {}
class Person: Object {
#Persisted(primaryKey: true) var id: Int = 0
#Persisted var name: String = ""
}
Usage would be like this:
let realm = try! Realm()
try! realm.write {
person.update(\.name, to: "BOB")
}
EDIT: things described below do not work like expected.
eg. if name = "Tom" and later the same value is assigned ( self.name = "Tom") it will be treated as modification.
It turns out that YES, Realm can be smart about it!
the clue sits in update parameter in .add function.
using .modified will result in smart data write.
Excerpt from documentation:
/**
What to do when an object being added to or created in a Realm has a primary key that already exists.
*/
#frozen public enum UpdatePolicy: Int {
/**
Throw an exception. This is the default when no policy is specified for `add()` or `create()`.
This behavior is the same as passing `update: false` to `add()` or `create()`.
*/
case error = 1
/**
Overwrite only properties in the existing object which are different from the new values. This results
in change notifications reporting only the properties which changed, and influences the sync merge logic.
If few or no of the properties are changing this will be faster than .all and reduce how much data has
to be written to the Realm file. If all of the properties are changing, it may be slower than .all (but
will never result in *more* data being written).
*/
case modified = 3
/**
Overwrite all properties in the existing object with the new values, even if they have not changed. This
results in change notifications reporting all properties as changed, and influences the sync merge logic.
This behavior is the same as passing `update: true` to `add()` or `create()`.
*/
case all = 2
}

How to replace/update dictionary in Array of Dictionary based on Type

I'm getting below JSON response from server, and displaying phone number on screen.
Now user can change/update any of phone number, so we have to update particular mobile number in same object and send it to server.
"phone_numbers": [
{
"type": "MOBILE",
"number": "8091212121"
},
{
"type": "HOME",
"number": "4161212943"
},
{
"type": "BUSINESS",
"number": "8091212344"
}
]
My model class is looks like this:
public struct Contact: Decodable {
public let phone_numbers: [Phone]?
}
public struct Phone: Decodable {
public let type: PhoneType?
public let number: String?
}
I'm struggling to update this JSON object for particular phone number.
For example, if I want to update BUSINESS number only in above array, What's best way to do it.
I'm using XCode 11 and Swift 5.
Because all your properties are defined as constants (let), nothing can be updated. You have to initialize and return a new Contact object with the updated phone numbers.
If you change the properties to var, then you can update:
public enum PhoneType: String, Decodable {
case mobile = "MOBILE"
case home = "HOME"
case business = "BUSINESS"
}
public struct Contact: Decodable {
public var phone_numbers: [Phone]?
mutating func update(phoneNumber: String, for type: PhoneType) {
guard let phone_numbers = self.phone_numbers else { return }
for (i, number) in phone_numbers.enumerated() {
if number.type == type {
self.phone_numbers![i].number = phoneNumber
}
}
}
}
public struct Phone: Decodable {
public var type: PhoneType?
public var number: String?
}
var contact = try! JSONDecoder().decode(Contact.self, from: jsonData)
contact.update(phoneNumber: "123456", for: .business)
I'm struggling to update this JSON object for particular phone number.
It shouldn't be a JSON object when you update it. Think of JSON as just a format for transferring data. Once transferred, you should parse it into something that you can work with, like an array of dictionaries or whatever. If you've done that, then more specific questions you might ask are:
How can I find a specific entry in an array?
How can I modify the fields of a struct?
How can I replace one entry in an array with another?
After looking at the definitions of your structures, I think the problem you're having probably has to do with how you've declared them:
public struct Phone: Decodable {
public let type: PhoneType?
public let number: String?
}
Because you used let to declare type and number, those fields cannot be changed after initialization. If you want the fields of a Phone struct to be modifiable, you need to declare them with var instead of let.
The same thing is true for your Contact struct:
public struct Contact: Decodable {
public let phone_numbers: [Phone]?
}
You've declared phone_numbers as an immutable array because you used let instead of var. If you want to be able to add, remove, or modify the array in phone_numbers, you need to use var instead.
The struct declarations you have right now work fine for reading the data from JSON because all the components of the JSON data are constructed using the values from the JSON. But again, you'll need to make those structs modifiable by switching to var declarations if you want to be able to make changes.
There are a couple of ways to approach this (I'm assuming PhoneType is an enum you have somewhere)
You can iterate over the array and guard for only business numbers, like so
for phone in phone_numbers{
guard phone.type == .MOBILE else { continue }
// Code that modifies phone
}
You can filter and iterate over the array, like so
phone_numbers.filter {$0.type == .BUSINESS }.forEach { phone in
// Modify phone here
}
You can then modify the right value in the array with it's index, like this
for (phoneIndex, phone) in phone_numbers.enumerated() {
guard phone.type == .BUSINESS else { continue }
phone_numbers[phoneIndex].type = ANOTHER_TYPE
}
Some can argue that the second is preferred over the first, because it is an higher order function, but in my day to day activities, I tend to use both and believe that this is a matter of taste

How would one create a List<AnyRealmObject> in Swift?

OK, first, I know that there is no such thing as AnyRealmObject.
But I have a need to have something the behaves just like a Realm List, with the exception that any kind of Realm Object can be added to the list -- they don't all have to be the same type.
Currently, I have something like this:
enter code here
class Family: Object {
var pets: List<Pet>
}
class Pet: Object {
var dog: Dog?
var cat: Cat?
var rabbit: Rabbit?
}
Currently, if I wanted to add in, say, Bird, I'd have to modify the Pet object. I don't want to keep modifying that class.
What I really want to do is this:
class Family: Object {
var pets: List<Object>
}
Or, maybe, define a Pet protocol, that must be an Object, and have var pets: List<Pet>
The point is, I want a databag that can contain any Realm Object that I pass into it. The only requirement for the databag is that the objects must be Realm Objects.
Now, since Realm doesn't allow for this, how could I do this, anyway? I was thinking of creating something like a Realm ObjectReference class:
class ObjectReference: Object {
var className: String
var primaryKeyValue: String
public init(with object: Object) {
className = ???
primaryKeyValue = ???
}
public func object() -> Object? {
guard let realm = realm else { return nil }
var type = ???
var primaryKey: AnyObject = ???
return realm.object(ofType: type, forPrimaryKey: primaryKey)(
}
}
The stuff with the ??? is what I'm asking about. If there's a better way of doing this I'm all ears. I think my approach is ok, I just don't know how to fill in the blanks, here.
(I'm assuming that you are writing an application, and that the context of the code samples and problem you provided is in terms of application code, not creating a library.)
Your approach seems to be a decent one given Realm's current limitations; I can't think of anything better off the top of my head. You can use NSClassFromString() to turn your className string into a Swift metaclass object you can use with the object(ofType:...) API:
public func object() -> Object? {
let applicationName = // (application name goes here)
guard let realm = realm else { return nil }
guard let type = NSClassFromString("\(applicationName).\(className)") as? Object.Type else {
print("Error: \(className) isn't the name of a Realm class.")
return nil
}
var primaryKey: String = primaryKeyValue
return realm.object(ofType: type, forPrimaryKey: primaryKey)(
}
My recommendation is that you keep things simple and use strings exclusively as primary keys. If you really need to be able to use arbitrary types as primary keys you can take a look at our dynamic API for ideas as to how to extract the primary key value for a given object. (Note that although this API is technically a public API we don't generally offer support for it nor do we encourage its use except when the typed APIs are inadequate.)
In the future, we hope to offer enhanced support for subclassing and polymorphism. Depending on how this feature is designed, it might allow us to introduce APIs to allow subclasses of a parent object type to be inserted into a list (although that poses its own problems).
This may not be a complete answer but could provide some direction. If I am reading the question correctly (with comments) the objective is to have a more generic object that can be the base class for other objects.
While that's not directly doable - i.e. An NSObject is the base for NSView, NSString etc, how about this...
Let's define some Realm objects
class BookClass: Object {
#objc dynamic var author = ""
}
class CardClass: Object {
#objc dynamic var team = ""
}
class MugClass: Object {
#objc dynamic var liters = ""
}
and then a base realm object called Inventory Item Class that will represent them
class InvItemClass: Object {
#objc dynamic var name = ""
#objc dynamic var image = ""
#objc dynamic var itemType = ""
#objc dynamic var book: BookClass?
#objc dynamic var mug: MugClass?
#objc dynamic var card: CardClass?
}
then assume we want to store some books along with our mugs and cards (from the comments)
let book2001 = BookClass()
book2001.author = "Clarke"
let bookIRobot = BookClass()
bookIRobot.author = "Asimov"
let item0 = InvItemClass()
item0.name = "2001: A Space Odyssey"
item0.image = "Pic of Hal"
item0.itemType = "Book"
item0.book = book2001
let item1 = InvItemClass()
item1.name = "I, Robot"
item1.image = "Robot image"
item1.itemType = "Book"
item1.book = bookIRobot
do {
let realm = try Realm()
try! realm.write {
realm.add(item0)
realm.add(item1)
}
} catch let error as NSError {
print(error.localizedDescription)
}
From here, we can load all of the Inventory Item Objects as one set of objects (per the question) and take action depending on their type; for example, if want to load all items and print out just the ones that are books.
do {
let realm = try Realm()
let items = realm.objects(InvItemClass.self)
for item in items {
switch item.itemType {
case "Book":
let book = item.book
print(book?.author as! String)
case "Mug":
return
default:
return
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
As it stands there isn't a generic 'one realm object fits all' solution, but this answer provides some level of generic-ness where a lot of different object types could be accessed via one main base object.

How can i have custom (Computed) property in objectmapper?

I am using Objectmapper and Realm for my project.
I have an object like following
class File
{
dynamic var name
dynamic var folder
dynamic var path // This is not coming from JSON // this should be combination of both name+folder
}
I thought of writing a computed property to achieve this but Realm does not support computed properties as primary key.
But I should use this as primary key. Is there any way I can manipulate to add that value after coming from server response.
Note: I am using AlamofireObjectMapper.
I am using the following method which parses the server response and gives me the model object.
Alamofire.request(router).responseObject{ (response: DataResponse<T>) in
{
let myModel = response.result.value // Parsed object
===== What can i do here to achieve my requirement=====
}
You should really consider having some kind of id as the primary key and not computing it from other properties (what happens if they are empty or the computation goes wrong? You'd be left without a valid primary key).
However, if you really need to, you could try
let realm = try Realm()
try realm.write {
items.forEach({ (item) in
item.path = item.name + item.folder
}
realm.add(items, update: true)
}
and don't forget to define path as the primary key in the File class:
class File
{
dynamic var name
dynamic var folder
dynamic var path // This is not coming from JSON // this should be combination of both name+folder
override static func primaryKey() -> String? {
return "path"
}
}

Add arrays to Realm with swift 3

I'm new in Realm and I tried to add an Array as I did with strings and I ended up with some errors. So after a little search I found out a solution:
class Sensors : Object {
dynamic var name = ""
dynamic var message = ""
var topic: [String] {
get {
return _backingNickNames.map { $0.stringValue }
}
set {
_backingNickNames.removeAll()
_backingNickNames.append(objectsIn: newValue.map({ RealmString(value: [$0]) }))
}
}
let _backingNickNames = List<RealmString>()
override static func ignoredProperties() -> [String] {
return ["topic"]
}
}
class RealmString: Object {
dynamic var stringValue = ""
}
This is working very good, now I want to add another array inside this class.
If someone knows any other ways to add arrays with realm please share it.
Thanks in advance
As a general rule it's way more efficient to use the one-to-many relationships provided by Realm instead of trying to emulate them by using arrays (Realm's collections are lazy, the objects contained are instantiated only when needed as opposed to plain Swift arrays).
In your case, if I understand correctly what you're trying to do, you want to add [RealmString] Swift arrays to the _backingNickNames list.
Why not use the append(objectsIn:) method of Realm's List class (see here), like this:
// Dog model
class Dog: Object {
dynamic var name = ""
dynamic var owner: Person?
}
// Person model
class Person: Object {
dynamic var name = ""
dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
let dogs = List<Dog>()
}
let jim = Person()
let dog1 = Dog()
let dog2 = Dog()
// here is where the magic happens
jim.dogs.append(objectsIn: [dog1, dog2])
If you want to do the opposite (convert from a List to an Array) just do :
let dogsArray = Array(jim.dogs)
• • • • • • • •
Back to your own posted solution, you could easily refactor the model to accommodate this. Each Sensor object could have several Topic and several Message objects attached.
Just ditch the message and topic computed properties and rename topicV and messageV to topics and messages respectively. Also rename RealmString to Topic and RealmString1 to Message.
Now, you could easily iterate through the, say, topics attached to a sensor like this :
for topic in sensor1.topics { ... }
Or if you want to attach a message to a sensor you could do something like this (don't forget to properly add the newly created object to the DB first):
let message1 = Message()
message1.stringValue = "Some text"
sensor2.messages.append(message1)
So, no need to use intermediary Swift Arrays.
After testing I managed to add another array like that:
class Sensors : Object {
dynamic var type = ""
dynamic var name = ""
dynamic var badge = 0
var topic: [String] {
get {
return topicV.map { $0.stringValue }
}
set {
topicV.removeAll()
topicV.append(objectsIn: newValue.map({ RealmString(value: [$0]) }))
}
}
var message: [String] {
get {
return messageV.map { $0.stringValue1 }
}
set {
messageV.removeAll()
messageV.append(objectsIn: newValue.map({ RealmString1(value: [$0]) }))
}
}
let topicV = List<RealmString>()
let messageV = List<RealmString1>()
override static func ignoredProperties() -> [String] {
return ["topic", "message"]
}
}
class RealmString: Object {
dynamic var stringValue = ""
}
class RealmString1: Object {
dynamic var stringValue1 = ""
}
What bogdanf has said, and the way you've implemented it are both correct.
Basic value types aside, Realm can only store references to singular Realm Object objects, as well as arrays of Objects using the List type. As such, if you want to save an array of types, it's necessary to encapsulate any basic types you want to save (like a String here) in a convenience Realm Object.
Like bogdanf said, it's not recommended to convert Realm Lists to standard Swift arrays and back again, since you lose the advantages of Realm's lazy-loading features (which can cause both performance and memory issues), but memory issues can at least be mitigated by enclosing the code copying data out of Realm in an #autoreleasepool block.
class MyObject: Object {
dynamic var childObject: MyObject?
let objectList = List<MyObject>()
}
So in review, it's best practice to work directly with Realm List objects whenever possible, and to use #autoreleasepool any time you do actually want to loop through every child object in a Realm. :)

Resources