I have a parent-child relationship, and the children need to wrap into array how can I do it?
event.testPrices = release.testPrices
final class Event: Content {
var id: String
var inProgress: Bool?
var name: String
var purpose: String?
var testPrices: [TestPrice]
init(id: String, name: String) {
self.id = id
self.name = name
}
}
extension Release {
var testPrices: Children<Release, TestPrice> {
return children(\.releaseId)
}
}
The assignment gives the error:
Cannot assign value of type 'Children' to type '[TestPrice]'
You can use a query to form the Future array and then map it. Assuming you are in some controller/route where event contains the appropriate Event and release contains the appropriate Release, try this:
{
release, event in
_ = release.testPrices.query(on:request).all().map { testP in
// testP is now [TestPrice]
event.testPrices = testP
}
}
Related
I have my model called Restaurant:
realm object on kotlin:
class Restaurant : RealmObject {
#PrimaryKey
var _id: ObjectId = ObjectId.create()
var name: String = ""
var adress: String? = null
}
I want to use the _id property. But for that I need to convert to a String in SwiftUI
I tried: restaurant_id as! String, it does not work,
Is is related to this post: https://www.mongodb.com/community/forums/t/swift-convert-objectid-to-string/121829
No real answers there
Any other solutions?
the error when using .toHexString(): Value of type 'any Library_baseObjectId' has no member 'toHexString':
the type of _id in this case:
The error when trying: "\(restaurant._id.stringValue)"
I solved this by adding a getter to the class:
class Restaurant : RealmObject {
#PrimaryKey
var _id: ObjectId = ObjectId.create()
var name: String = ""
var adress: String? = null
fun getID() : String{
return _id.toString()
}
}
ObjectID's in Swift have a stringvalue property which returns the ObjectID as a string
You could do this
let idAsAsString = someRealmObject._id.stringValue
Or for kotlin and other SDK;s
let idAsString = object._id.toString()
In SwiftUI, the id can be accessed in a similar way, and then use the value to init a new string
let x = someRealmObject._id.stringValue
let y = String(stringLiteral: x)
let _ = print(x, y)
Text("\(x) \(y)")
and the output will be
6376886a1dbb3c142318771c 6376886a1dbb3c142318771c
in the console and the same in the Text in the UI
Here's some SwiftUI showing the use
List {
ForEach(myModel) { model in
NavigationLink(destination: SecondView(navigationViewIsActive: $navigationViewIsActive, selectedEntryToShow: model))
{
Text("\(model._id.stringValue)") //ormodel._id.toString()
}
}
}
This Swift model works perfectly and the string of ObjectId can be read per above.
class Restaurant: Object {
#Persisted(primaryKey: true) var _id: ObjectId
#Persisted var name: String = ""
#Persisted var adress: String? = null
}
Consider the below Observable Object.
class User: ObservableObject {
#Published var age: Int
#Published var name: String {
didSet {
objectWillChange.send()
}
}
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
The below code prints blank value or Void block. Any reason why? If we change Integer value age it should simply print that value.
let userJohnCancellable = userJohn.objectWillChange.sink { val in
print("Changed Value \(val)")
}
userJohn.age = 21
userJohn.age = 39
We can try to print the values in the closure using userJohn.age. But why does val not return a Integer value in this case.
Also what would be the best way to handle sink changes for age and name, both, one is String other is Int.
When you look in the documentation for ObservableObject you will find thatobjectWillChange is ObservableObjectPublisher
/// A publisher that emits before the object has changed.
public var objectWillChange: ObservableObjectPublisher { get }
which in turn is defined as having an output of type Void:
final public class ObservableObjectPublisher : Publisher {
/// The kind of values published by this publisher.
public typealias Output = Void
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
public typealias Failure = Never
}
There is no need to send objectWillChange from didSet - each time any of the #Published values changes objectWillChange will emit a value.
If you want to get notified when a particular property marked as #Published changes and receive the new value you have to subscribe to that particular property:
let userJohn = User(name: "Johnny", age: 17)
let objectWillChangeCancellable = userJohn
.objectWillChange
.sink {
print("object will change")
}
let ageCancellable = userJohn
.$age
.sink { age in
print("new value of age is \(age)")
}
let nameCancellable = userJohn
.$name
.sink { name in
print("new value of name is \(name)")
}
This will get printed:
new value of age is 17
new value of name is Johnny
if you add:
userJohn.name = "John"
you will see the following printed:
object will change
new value of name is John
if you add:
userJohn.age = 21
you will see the following printed:
object will change
new value of age is 21
You seem to be confused about ObservableObject. It is for use with SwiftUI. But your code is not SwiftUI code, so you don't need ObservableObject and you really can't use it in any meaningful way. If the goal is to be able to subscribe to the properties of User so as to be notified when one of them changes, then it suffices to make those properties Published:
class User {
#Published var age: Int
#Published var name: String
init(age: Int, name: String) {
self.age = age; self.name = name
}
}
Here's an example of using it; I will assume we have a user property of a UIViewController:
class ViewController: UIViewController {
var cancellables = Set<AnyCancellable>()
var user = User(age: 20, name: "Bill")
override func viewDidLoad() {
super.viewDidLoad()
user.$age.sink {print("age:", $0)}.store(in: &cancellables)
user.$name.sink {print("name:", $0)}.store(in: &cancellables)
}
}
If this view controller's user has its age or name changed, you'll see the print output in the console.
If the question is how to handle both changes in a single pipeline, they have different types, as you observe, so you'd need to define a union so that both types can come down the same pipeline:
class User {
#Published var age: Int
#Published var name: String
enum Union {
case age(Int)
case name(String)
}
var unionPublisher: AnyPublisher<Union, Never>?
init(age: Int, name: String) {
self.age = age; self.name = name
let ageToUnion = $age.map { Union.age($0) }
let nameToUnion = $name.map { Union.name($0) }
unionPublisher = ageToUnion.merge(with: nameToUnion).eraseToAnyPublisher()
}
}
And again, here's an example of using it:
class ViewController: UIViewController {
var cancellables = Set<AnyCancellable>()
var user = User(age: 20, name: "Bill")
override func viewDidLoad() {
super.viewDidLoad()
user.unionPublisher?.sink { union in
switch union {
case .age(let age): print ("age", age)
case .name(let name): print ("name", name)
}
}.store(in: &cancellables)
}
}
Again, change the user property's name or age and you'll get an appropriate message in the console.
I return an instance from a get-only subscript, but I cannot assign to a property of the returned instance
struct Student {
var id: String
var attendance = 0
var absence = 0
}
class StudentStore {
var students = [Student]()
subscript(id: String) -> Student? {
students.first(where: { $0.id == id } )
}
}
var studentStore = StudentStore()
var newStudent = Student(id: "2014901001")
studentStore.students.append(newStudent)
studentStore["2014901001"]?.attendance = 1 // error: Cannot assign to property: subscript is get-only
The subscription doesn't have a setter. What you have is the implicit getter which is a short form of
subscript(id: String) -> Student? {
get {
students.first(where: { $0.id == id } )
}
}
But it's impossible to update a value type item in an array with key subscription anyway.
An alternative is to add an update function to the store which takes the id, a keyPath and a value
func updateStudent<Value>(withID id: String, keyPath: WritableKeyPath<Student,Value>, value: Value) {
guard let index = students.firstIndex(where: {$0.id == id}) else { return }
students[index][keyPath: keyPath] = value
}
Getting the index is mandatory to be able to modify the item in the array directly.
Then you can replace
studentStore["2014901001"]?.attendance = 1
with
studentStore.updateStudent(withID: "2014901001", keyPath: \.attendance, value:1)
When you do
studentStore["2014901001"]?.attendance = 1
You are trying to set one of the Student objects in the students array, aren't you?
However, the subscript getter doesn't return (a reference to) one of the objects in the students array. Due to the value semantics of the Student struct, you return a copy of the Student object that is in the students array. You can't achieve what you want if you just set the attendance of that copy. Therefore, the Swift compiler compiles your code to something like this:
let copy = studentStore["2014901001"]
copy?.attendance = 1
studentStore["2014901001"] = copy
You can see that it sets the setter with the modified copy again. But your subscript does not have a setter, which is where the error comes from.
To solve this, you can make Student a class, which has reference semantics. This enables you to return the Student object that is in the students array, allowing the caller of the subscript to set their attendance.
class Student {
var id: String
var attendance = 0
var absence = 0
init(id: String, attendance: Int = 0, absence: Int = 0) {
self.id = id
self.attendance = attendance
self.absence = absence
}
}
Alternatively, you can add a setter. However, that would allow nonsense like this:
studentStore["1"] = Student(id: "2")
You need to decide what you want to happen here.
Error show exactly what you have to change. Because subcript will return something likes:
let updateStudent = studentStore["studentStore"]
You can not change with let
So update with this:
if var updateStudent = studentStore["studentStore"] {
updateStudent.attendance = 1
}
I'm trying to implement the MVVM pattern using Bond in a test project.
The idea is simple:
Define an abstraction which the viewModel then uses.
Make a concrete type from this abstraction.
Inject this concrete type in the viewModel.
This is my code so far:
// 1.
protocol Commentable {
var id: Int { get }
var name: String { get }
var body: String { get }
}
// 2.
struct Comment: Commentable {
var id: Int
var name: String
var body: String
}
// 3.
struct CommentViewModel {
private let comment: Commentable
init(comment: Commentable) {
self.comment = comment
}
public var id: Observable<Int> {
return self.comment.id
}
}
Xcode shows the following error when I try to return self.comment.id:
Cannot convert return expression of type 'Int' to return type
'Property
This makes sense - comment.id is an Int and self.id is an Observable<Int>. How do make this work though, since I don't want to define the properties in my Comment type as Observable.
Fixed it - just had to change the syntax:
struct CommentViewModel {
private let comment: Observable<Commentable>
init(comment: Commentable) {
self.comment = Observable(comment)
}
public var id: Observable<Int> {
return Observable(comment.value.id)
}
}
I'm trying to print all the values from an object that inherits from a class, here is my example:
I create the class:
class Pokemon {
var name: String?
var type: String?
var level: Int?
var exp = 0.0
}
Create the object and assign some values:
var pikachu = Pokemon()
pikachu.name = "Pika Pika"
pikachu.level = 1
pikachu.type = "electricity"
pikachu.exp = 0
Now I would like to loop through all the pikachu object attributes and print the values. I'm thinking in a for each loop but I'm not sure how to implement it.
I know I can do something like this:
func printStats(pokemon: Pokemon) {
if pokemon.name != nil {
print(" name: \(pokemon.name!)\n level:\(pokemon.level!)\n type:\(pokemon.type!)\n exp: \(pokemon.exp!)")
}
}
printStats(pokemon: pikachu)
output:
name: Pika Pika
level:1
type:electricity
exp: 0.0
But I just want to loop through all values, instead of explicit writing every attribute in the function.
I found it the way of doing it:
let pokeMirror = Mirror(reflecting: pikachu)
let properties = pokeMirror.children
for property in properties {
print("\(property.label!) = \(property.value)")
}
output:
name = Optional("Pika Pika")
type = Optional("electricity")
level = Optional(1)
exp = Optional(0.0)
and if you want to remove the "Optional" just initialize the attributes.
Looks like a duplicate of Does Swift support reflection?
Alternatively, you can use a dictionary to store the attributes of Any? type.
e.g.
class Pokemon {
var attributes = [String:Any?]()
}
var pikachu = Pokemon()
pikachu.attributes["name"] = "Pika Pika"
pikachu.attributes["level"] = 1
pikachu.attributes["type"] = "electricity"
pikachu.attributes["exp"] = 0
func printStats(pokemon: Pokemon) {
pokemon.attributes.forEach { key, value in
if let value = value {
print("\(key): \(value)")
}
}
}
In Swift 5 you can create a new func in your class:
func debugLog() {
print(Mirror(reflecting: self).children.compactMap { "\($0.label ?? "Unknown Label"): \($0.value)" }.joined(separator: "\n"))
}
And then call it with MyObject().debugLog()
use Mirror API to get instance's properties
if you are developing iOS app, using NSObject, you may want to override description. Then can use print to print the instance.
A mirror describes the parts that make up a particular instance, such as the instance’s stored properties, collection or tuple elements, or its active enumeration case.
class YourClass: NSObject {
public override var description: String {
var des: String = "\(type(of: self)) :"
for child in Mirror(reflecting: self).children {
if let propName = child.label {
des += "\(propName): \(child.value) \n"
}
}
return des
}
}
let instance = YourClass()
print(instance)
see more in Reflection in Swift