ios swift models structure with realm database - ios

I am developing my iOs App and I am using Realm database. As I am totally new to ios developing (also swift and xcode) I have question about structuring data (I've already read some general project structure guidelines but couldn't find the answer). My thinking is connected with Java structures
For Realm databases (RealmSiwft) I created a model like this:
#objcMembers class Patient: Object {
dynamic var patientId:Int = 0
dynamic var refNumber:String = ""
convenience init(id:Int, refNumber:String){
self.init()
self.patinetID = id
self.refNumber = refNumber
}
}
Now, it looks just like a POJO class in Java. But as I learned, this model structure is made that way so it can be able to use Realm.
So the question is, if I need somewhere else in my project to use Patient objects, is this Realm-POJO-model good to use? I mean, should I use it just like a normal Model even when I dont need to make database operations on it? Or should I make this Realm model alike DAO class for databases operations and make another model class like Patient.swift for whenever I want to play with Patient without using databases (I hope not, cause it's so much code duplicating)
And what if I need variables in that Patient Model that won't be stored in database? Can I make it without dynamic? What about init than? That blows my mind, as far as I learn swift it seems so ugly and unstructured, or I just can't switch to it yet...

if I need somewhere else in my project to use Patient objects, is this
Realm-POJO-model good to use?
even when I dont need to make database operations on it?
You can use your Patient object without savings to the DB, move them to different controllers and so on.
what if I need variables in that Patient Model that won't be stored
in database?
Look to ignoredProperties() method.
Can I make it without dynamic?
No you can't because of Realm based on Objective-C object, so this is necessary type.
What about init than?
You can create different Constructors methods, look to the Initialization doc. In case with Realm you should setup values to noticed variables (if you don't give them Default Property Values)
Your class should look like this:
class Patient: Object {
// MARK: - Properties
#objc dynamic var patientId: Int = 0
#objc dynamic var refNumber: String = ""
// MARK: - Meta
// to set the model’s primary key
override class func primaryKey() -> String? {
return "patientId"
}
//Ignoring properties
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
//It's ok
convenience init(id:Int, refNumber:String){
self.init()
self.patientId = id
self.refNumber = refNumber
}
}
All other detail information you can find in: realm docs
Also you can extend you base code with swift extension:
extension Patient {
var info: String {
return "\(patientId) " + refNumber
}
func isAvailableRefNumber() -> Bool {
return refNumber.length > 6
}
}

Related

Dynamically retrieving data of different types based on user input using Swift

New to Swift, I am trying to build an iOS app that allows a user to construct dynamic reports. I have created several model classes meant to represent various types of data to report on, e.g. Widgets and Gadgets, and associated service classes for these models containing the logic needed to retrieve their data. Examples for a model and service here:
class Widget: BaseModel, Equatable, Identifiable {
let id: UUID
let value: Int
let timeStamp: Date
init(rawData: RawData) {
self.value = rawData.quantity
self.timeStamp = rawData.date
self.id = UUID()
}
func toString() -> String {
return "\(self.value) widgets"
}
static func == (lhs: Widget, rhs: Widget) -> Bool {
return lhs.id == rhs.id
}
}
class WidgetService: BaseService {
let queryService: QueryService = QueryService()
func fetchSamplesInDateRange(dates: (startDate: Date, endDate: Date)?) async -> [Widget] {
// get samples and return them
}
}
These are based off of two protocols with associated types, BaseModel and BaseService. What I'm trying to do in this very early proof of concept stage is implement a simple view that will allow a user to select a type of data (corresponding to one of my Models) and run a query for all data for that Model within a date range. But I am completely stuck on how to implement the model selection.
I have a basic view file I've been working with that just has one button that executes a fetch function, populating a state variable results.
func fetchData() async -> Void {
let modelService: some BaseService = // not sure what to do here
let result: [some BaseModel] = await modelService.fetchSamplesInDateRange(dates: nil)
results = result
}
Because BaseModel and BaseService have associated types they can't be used in the way above as type constraints, I know, I put them there to give an idea of what I'm trying to do.
I had thought to put the class names as strings, choosable via options in a Picker, and then use something like NSClassFromString to get the right class for the service, but ultimately this just doesn't seem workable as I have to declare the concrete service class type at some point, this solution just moves around where I run into the Protocol 'BaseModel' can only be used as a generic constraint because it has Self or associated type requirements error.
Any idea what I'm missing here? This sort of thing using base classes, extensions, generics, just doesn't seem like it should be this difficult as other languages handle it fine (though admittedly with less type safety).

Equatable Testing in Swift

I'm looking for suggestions on good practices to test the equality of swift objects
If I have a complex object with many properties it's easy to miss the equality implementation.
What are the good practices to enforce the equality implementation?
More details:
Of course, you can write tests to check that two value are equal when they should, but the problem here is that this can break easily.
You have a struct in your project, after six months another developer adds a property to that struct.
Assume that he forgets to add the property to the equality. The equality still works, the tests pass but your app will break when only that property changes. My question is, there is a way to avoid this problem?
In a non trivial app is really easy to end up having model objects with 10 values, and use nested model objects. So how can I keep under control the equality of my model objects? How can I reduce the human error risk in the equality implementation?
Possible solution:
One solution I have in mind is to write a script that looks at compile time for classes and struct that conform to a certain protocol
Diffable : Equatable {}
For all the classes/structs that adopt Diffable I'll check for the existance of a test file, i.e.:
For the class Foo I'll look for a test class Foo_Tests_Diffable
than for all the properties inside Foo I'll check for the existance of a test function that conform to the following name pattern
test<property name>Diffable
i.e.:
class Foo : Diffable {
var title: String
var color: UIColor
var tag: Tag
}
I'll check for the following tests inside Foo_Tests_Diffable
func testTitleDiffable {
// Test
}
func testColorDiffable {
// Test
}
func testTagDiffable {
// Test
}
If all the script finds all the expected tests than the compile step pass, otherwise it fails
But this solution it's time consuming and I don't know if I'll be able to implement it, so any suggestion is welcome
I would create unit tests specifically for this purpose.
class YourObjectEquatableTests: XCTestCase
{
func testPropertyOne()
{
var object1 = /* ... */
var object2 = /* ... */
object1.propertyOne = nil
object2.propertyOne = nil
XCTAssertEqual(object1, object2)
object1.propertyOne = "different value"
XCTAssertNotEqual(object1, object2)
/* etcetera */
}
func testPropertyTwo()
{
/* ... */
}
}
Note: if you write your test cases before implementing the Equatable details, you're actually doing TDD which is very cool!
I didn't try it a lot but Swift has Reflection capabilities.
https://appventure.me/2015/10/24/swift-reflection-api-what-you-can-do/
So in your case, you can mirror a class and then put some conditions in your test.
for example, you can set a fix number or attribute or methods for a class in your test.
let nbAttributes = 6
and then check it:
aMirror.children.count
I wrote a simple example in the playground and if I add a method variable, the children count increases. I don't know if it's possible to check the methods though.
class toto{
var io = 9
var tutu = "yo"
func test(){
}
}
let toto1 = toto()
let aMirror = Mirror(reflecting: toto1)
aMirror.children.count
another link:
Reflection in swift 2
Hope that helps :)
Yes, expanding on #Mikael's answer, you should probably use Reflection.
An example would be:
String(reflecting: obj1) == String(reflecting: obj2)

Best Approach to Refresh a Class [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Simply i want to know what is the Best Approach to refresh a class.Like when same class is used from diff-diff section of application where we pass some data to that class ,I can do it by simply taking a global variable and change it's value from other class's But i want to know some another approach which is best suitable for this kind of Things.
The best way to handle this kind of structure, where the class needs to be effectively shared between other MVCs, is to make the class a singleton. This means that only one instance of a class is ever used. That way if one MVC changes the class you don't need to sync the change because every MVC uses the same instance.
To make a class a singleton you do the following
Create a static var which is an instance of the class
Make the init function private so no other class can initialise it
Eg
class Singleton {
static var shared = Singleton()
var info = "singleton info variable"
private init() {}
func doSomething() {}
}
To access the class you do the following
Singleton.shared.doSomething()
or
let singletonInfo = Singleton.shared.info
You have two options:
(Simplified) - Either you create a new instance and pass it on:
class MyClass {
let name: String
init(name: String) { self.name = name }
}
class MyViewController: UIViewController {
let myClass = MyClass(name: "Hello World")
func actionThatCausesSegue() {
let copyOfMyClass = MyClass(name: self.myClass.name)
self.performSegueWithIdentifier(identifier: "MySegue", sender: copyOfMyClass)
}
}
Or you can use structs:
struct MyStruct {
let name: String
init(name: String) { self.name = name }
}
class MyViewController: UIViewController {
let myStruct = MyStruct(name: "Hello World")
func actionThatCausesSegue() {
// structs are always copied when passed around.
self.performSegueWithIdentifier(identifier: "MySegue", sender: self.myStruct)
}
}
Structs are always copied, when you pass them around. Here's the explanation from the Swift Programming Language Guide by Apple:
Structures and Enumerations Are Value Types
A value type is a type whose value is copied when it is assigned to a
variable or constant, or when it is passed to a function.
You’ve actually been using value types extensively throughout the
previous chapters. In fact, all of the basic types in Swift—integers,
floating-point numbers, Booleans, strings, arrays and dictionaries—are
value types, and are implemented as structures behind the scenes.
All structures and enumerations are value types in Swift.
You can read more about the differences here.
It depends on the situation, but I sometimes do this for views in iOS. For example when the data model changes you want the view to update, so I implement a method in the controller called configureThingyViewWithThingy which takes the new 'thingy' and updates the view. Then whatever mechanism you're using to monitor data model changes (KVO, notifications, etc.) will call this method to reconfigure the view with the new data.

Save EVObjects with CoreData

I need to save some data with CoreData. Generally thats not a problem at all. The problem is, that the data is created with EVReflection an therefore inherits the class EVObject. To save the gathered data to CoreData they have to also inherit NSManagedObject. The problem is that swift does not allow you to inherit multiple classes. Would appreciate any tips.
class Device : EVObject
{
var channel : [Channel] = [Channel]()
var name : String = ""
var ise_id : Int = 0
var unreach : Bool = false
var sticky_unreach : Bool = false
var config_pending : Bool = false
override internal func propertyMapping() -> [(String?, String?)] {
return [("name", "_name"), ("ise_id", "_ise_id"), ("unreach", "_unreach"), ("sticky_unreach", "_sticky_unreach"), ("config_pending", "_config_pending")]
}
}
You don't have to inherit. You can extend them. Example:
class User : NSManagedObject{
#NSManaged .....
}
//Extension
import EVReflection
extension User : EVReflectable { }
https://github.com/evermeer/EVReflection#extending-existing-objects
Note I'm not aware of EVReflection but I think this answer can generally apply.
Don't use multiple inheritance. Have two separate classes and a mechanism for creating/loading/updating one object from the other. Protocols may allow it to be done in a way that minimises translation boilerplate (possibly using valueForKey(_:) and setValue(_:forKey) if you can know the key names in a safe manner.
It may not even be even be necessary to have an NSManagedObject subclass but just have an instance of NSManagedObject in all your classes that is loaded/created/saved as necessary.
It depends on what functionality you want to use from EVReflection. Since NSManagedObject also has NSObject as it's base class you could use most functions by just setting NSManagedObject as your base class instead of EVObject.
The only thing you have to do is instead of calling EVObject functions directly, you have to implement the code snippets that are in that EVObject method. Almost any function there is just a convenience method that will call the corresponding EVReflection function.
If you have any questions in the future, then please also report this as an issue on GitHub.

Swift + Realm newbie: Problems with a simple Realm object and its initializers

I've been a long time Objective-C developer and heard about Realm some weeks ago. On the other hand I've always wanted to migrate little by little to Swift, so I created a small project involving Realm + Swift.
What does it mean? I'm a Swift + Realm newbie.
Anyway, I created a small demo/Proof of concept for a project I have in mind and I thought it had to be easier. But Xcode compiler says otherwise.
My problem is with one of my Objects' initializer(s).
My intentions were simple, but apparently Realm needs more initializers than I wanted.
The code of one of my Realm Objects is this:
import Foundation
import Realm
import RealmSwift
class Partida: Object
{
dynamic var id_partida: String
dynamic var inventario: Inventory?
required init ()
{
inventario = Inventory()
id_partida = "id_partida_test"
super.init()
}
required init(value: AnyObject, schema: RLMSchema) {
//fatalError("init(value:schema:) has not been implemented")
super.init(value: value, schema: schema)
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
//fatalError("init(realm:schema:) has not been implemented")
super.init(realm: realm, schema: schema)
}
override class func primaryKey() -> String? {
return "id_partida"
}
}
My original code only had the "normal" init initializer. But Xcode forced me to create two additional initializers more (value and realm ones).
If I compile the code I've just pasted above, Xcode complains in the 2nd and 3rd required initializers, specifically in the super.init part.
It says:
Property 'self.id_partida' not initialized at super.init call
I understand the meaning of it, but I don't know how to avoid the error because if I remove both super.init lines, the program crashes in runtime.
if I uncomment the fatalError lines, they also crashes in runtime.
In fact I don't want to use these 2 initializers. If I could, I wouldn't add them, but Xcode needs to, apparently. The only code I really want to add to my object init function is "the simple" init function, which was the only part of code considered mine.
I think I might have some concept misunderstandings in Realm + Swift + initializers.
I'm also having the feeling Xcode is forcing me to add code I don't need and/or I don't understand either.
Any help on understanding "required init" initializers in Realm will be more than welcome.
Official Realm + Swift documentation is beyond my knowledge as I don't understand many of its concepts even after re-reading them many times.
Google and StackOverflow haven't been really helpful this time...
Thanks.
Initializers in Swift definitely behave a bit differently to Objective-C, so I can definitely see the angle you're coming from here.
In this case though, since you're just using the initializer to set some default values, it's wholly un-necessary since you should be able to assign the default values to the properties themselves:
class Partida: Object
{
dynamic var id_partida = "id_partida_test"
dynamic var inventario: Inventory? = Inventory()
override class func primaryKey() -> String? {
return "id_partida"
}
}
Let me know if that still doesn't work! :)
Because it already has init () in Object class, you are using subclass of Object, so you already have its init in Realm object, you should give your var init value, like dynamic var id_partida: String = "id_partida_test", and then if you call let test = Partida() it already has your 2 init value, other init should be marked with convenience
When you save the Object to persistent store, it should be always have value, you can use Realm's optional then need read the documentation
Here's my sample Realm class so that u got the idea:
import Foundation
import RealmSwift
import SwiftyJSON
class ProjectModel: Object {
dynamic var id: Int = 0
dynamic var name: String = ""
//Dont need this, call init() already have value
required init() {
id = 0
name = ""
super.init()
}
convenience init(fromJson json: JSON!){
self.init()
if json == nil {
return
}
id = json["id"].intValue
name = json["name"].stringValue
}
override class func primaryKey() -> String? {
return "id"
}
}

Resources