How to use Realm Swift Init - swift

I am trying to use the init for Realm in Swift. I have tried the following
override init(value: AnyObject) {
super.init()
println("this is not called")
}
required init() {
super.init()
println("this is called")
}
I want to be able to pass an object into the initializer, however, I can't get the first function to be called.

My solution in Swift 3
Custom initializer:
class Branches: Object {
dynamic var key: String = NSUUID().uuidString
dynamic var formattedAddress: String = ""
dynamic var latitude: Double = 0.0
dynamic var longitude: Double = 0.0
convenience init(formattedAddress: String, latitude: Double, longitude: Double) {
self.init()
self.formattedAddress = formattedAddress
self.latitude = latitude
self.longitude = longitude
}
override static func primaryKey() -> String? {
return "key"
}
}

Overriding Object.init() and Object.init(_:) are not yet supported in Realm Swift, but you can follow https://github.com/realm/realm-cocoa/issues/1849 for updates!

Here's my answer for iOS 13, Swift 5.
Being totally honest with you, I had no idea what I was doing, but I managed it to work by doing so:
class Account: Object {
#objc dynamic var name: String = ""
#objc dynamic var balance: Double = 0.0
#objc dynamic var savings: Double = 0.0
#objc dynamic var available: Double = 0.0
init(name: String, balance: Double){
self.name = name
self.balance = balance
self.savings = 0.0
self.available = balance
}
required init() {
//fatalError("init() has not been implemented")
}
}
Keep in mind that I'm totally new with Swift (about 3 months top), and I don't know the consequences of doing that. But my project is working just fine. Everything is being saved and retrieved perfectly.

Related

Using lazy variable vs. Initializing - Swift

I'm making an app with thousands of inputs.
I have a model class like this
class Waterz {
var id: String = ""
var entitle: String? = nil
var artitle: String? = nil
var date: [String]? = nil
var frequency: [daysOfWeek]? = nil
var subMenus: Array<Waterz>? = nil
var location: String? = nil
var audio: [reciterNames]? = nil
var menuIcon: String? = nil
init() {
}
convenience init(id: String, entitle: String, artitle: String, date: [String]?, frequency: [daysOfWeek]?, location: String?, audio: [reciterNames]?) {
self.init()
self.id = id
self.entitle = entitle
self.artitle = artitle
self.date = date
self.frequency = frequency
self.location = location
self.audio = audio
}
}
And then I have my filling-up class. My filling up class has thousands of variables, so I wanted to see if I should use lazy vars or declare each variable, then initialize it in the init() like this
class Database {
var duaKumayl: Waterz
init() {
duaKumayl = .init(...)
}
}
Or should I use lazy vars? Using my current method of declaring then initializing will take forever because I have thousands of variables.
Try to do pagination within app , if you have thousands of variables . This will reduce the show up time for the data you are presenting in UI.
In such cases as i mentioned above , you should use initialization . But if your evaluation of data takes too long use lazy variables.

Get notified when value in List is updated using realm

I am trying to write an application in OS X using a Realm database. I want to trigger notification when where there is change in value of List which is inside another object
Below is the Class
final class Profile: Object {
#objc dynamic var gradient1 = ""
#objc dynamic var gradient2 = ""
#objc dynamic var fontColor = ""
#objc dynamic var selectedFont = ""
#objc dynamic var selectedTitleFont = ""
#objc dynamic var fontFamily = ""
#objc dynamic var name = ""
#objc dynamic var shortbio = ""
#objc dynamic var avatarSource = ""
#objc dynamic var userid = ""
#objc dynamic var email = ""
var features = List<Features>()
var socialLinkButtons = List<SocialLinkButtons>()
#objc dynamic var appSelectedMetaData : AppMetaData? = nil
override static func primaryKey() -> String?{
return "userid"
}
}
final class Features: Object {
#objc dynamic var uuid = ""
#objc dynamic var id = ""
#objc dynamic var label = ""
#objc dynamic var screen = ""
#objc dynamic var active = false
override static func primaryKey() -> String?{
return "id"
}
convenience init(id: String, uuid: String, label: String, screen: String, active: Bool) {
self.init()
self.id = id
self.uuid = uuid
self.label = label
self.screen = screen
self.active = active
}
}
I want to trigger notifications whenever value inside feature is updated.
You can use Realm Collection Notifications to achieve your goals. You just need to make sure that you store the returned NotificationToken in a variable that doesn't get deallocated until you don't actually need to receive the notifications anymore and that you call .invalidate() on the token when you no longer want to receive notifications.
func observeFeatureChanges(in profile:Profile) -> NotificationToken {
let notificationToken = profile.features.observe { changes in
switch changes {
case .update(_, deletions: let deletionIndices, insertions: let insertionIndices, modifications: let modIndices):
print("Objects deleted from indices: \(deletionIndices)")
print("Objects inserted to indices: \(insertionIndices)")
print("Objects modified at indices: \(modIndices)")
case .error(let error):
print(error)
case .initial(let type):
print(type)
}
}
return notificationToken
}

Realm Swift Composite Keys for Optional Properties

Is there any way I could make composite keys for a Realm class with optional properties?
for example:
class Item: Object {
dynamic var id = 0
let importantNumber = RealmOptional<Int>()
let importantNumber2 = RealmOptional<Int>()
func setCompoundID(id: Int) {
self.id = id
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber(importantNumber: Int) {
self.importantNumber = importantNumber
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber2(importantNumber2: Int) {
self.importantNumber2 = importantNumber2
compoundKey = compoundKeyValue()
}
dynamic lazy var compoundKey: String = self.compoundKeyValue()
override static func primaryKey() -> String? {
return "compoundKey"
}
func compoundKeyValue() -> String {
return "\(id)\(importantNumber)\(importantNumber2)"
}
}
When I write my code like this, the compiler complains that I can't assign to my constant properties and it recommends me changing 'let' to 'var'; however, according to the Realm Swift Documentation, I need to have optional properties set as constants.
I'm not sure if this is even possible because I can't find anything in the Realm documentation about optional primary keys.
You need to set RealmOptional's value member. RealmOptional properties cannot be var because Realm can't detect assignment to property types that cannot be represented by the Objective-C runtime, which is why RealmOptional, List and LinkingObjects properties must all be let.
class Item: Object {
dynamic var id = 0
let importantNumber = RealmOptional<Int>()
let importantNumber2 = RealmOptional<Int>()
func setCompoundID(id: Int) {
self.id = id
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber(importantNumber: Int) {
self.importantNumber.value = importantNumber
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber2(importantNumber2: Int) {
self.importantNumber2.value = importantNumber2
compoundKey = compoundKeyValue()
}
dynamic lazy var compoundKey: String = self.compoundKeyValue()
override static func primaryKey() -> String? {
return "compoundKey"
}
func compoundKeyValue() -> String {
return "\(id)\(importantNumber)\(importantNumber2)"
}
}

Realm map clustering

To create your own data to present in realm map you need Model which looks like from example :
public class ABFRestaurantObject: Object {
public dynamic var businessId: String?
public dynamic var name: String?
public dynamic var address: String?
public dynamic var city: String?
public dynamic var state: String?
public dynamic var postalCode: String?
public dynamic var latitude: Double = 37.7859547
public dynamic var longitude: Double = -122.4024658
public dynamic var phoneNumber: String?
public let violations = List<ABFViolationObject>()
public let inspections = List<ABFInspectionObject>()
override public static func primaryKey() -> String? {
return "businessId"
}
}
What means this primaryKey ? Then how could I load my own data into it ? Of course omitting fact that you have to create your own model with custom propertiese ?
Thanks in advance!
EDIT
I am using Realm map kit clustering
I've created model :
class Test: Object {
public dynamic var id = 0
public dynamic var latitude = 45.0889
public dynamic var longitude = 54.1565
override static func primaryKey() -> String? {
return "id"
}
}
Then in my map controller I've added following functions and declarations :
var positions = try! Realm().objects(Test.self)
var position:Test!
override func viewDidLoad() {
super.viewDidLoad()
addNewVehicle()
populateMap()
}
func addNewPosition() {
let realm = try! Realm() // 1
try! realm.write { // 2
let newPos = Test() // 3
newPos.latitude = 50.060363
newPos.longitude = 19.939983
realm.add(newPos) // 5
self.position = newPos // 6
}
}
func populateMap() {
mapView.removeAnnotations(mapView.annotations) // 1
let positions = try! Realm().objects(Test.self) // 2
// Create annotations for each one
for pos in positions { // 3
let coord = CLLocationCoordinate2D(latitude: pos.latitude, longitude: pos.longitude);
let pin = MapPin(coordinate: coord, title: "asd", subtitle: "asd")
mapView.addAnnotation(pin) // 4
}
}
And at the end simple class fro creating pins :
class MapPin : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
I would like to create few random pins and see if clustring works.
Atm i am getting an error in line :
safeObject->_coordinate = coordinate;
Thread 9: EXC_BAD_ACCESS(code=1,address=0x40)
po positions
Results<Test> (
[0] Test {
latitude = 50.060363;
longitude = 19.939983;
},
[1] Test {
latitude = 50.060363;
longitude = 19.939983;
},
[2] Test {
latitude = 50.060363;
longitude = 19.939983;
}
)
Then I've added as well to my AppDelegate.swift to the method didFinishLaunchingWithOptions :
let config = RLMRealmConfiguration.default()
config.schemaVersion = 1
config.migrationBlock = { (migration, oldSchemaVersion) in
}
RLMRealmConfiguration.setDefault(config)
Error: "Primary key property 'Test.id' has duplicate values after migration."
From the REALM documentation.
Override Object.primaryKey() to set the model’s primary key. Declaring
a primary key allows objects to be looked up and updated efficiently
and enforces uniqueness for each value. Once an object with a primary
key is added to a Realm, the primary key cannot be changed.
If you would like to create own Realm model simply create class with Object extension.
Example:
class User: Object {
dynamic var id = 0
dynamic var name = ""
dynamic var email = ""
}

What does 'Type not allowed here' mean?

I'm using Swift 2.3 and trying to create a callback function that will cause my UIViewController to update itself with the updated view model but I'm getting a compile error in my view model class - 'Type not allowed here'. I'm getting other errors as well but they all seem to be cause by a fundamental problem with my CalculatorViewModel class. It's worth noting that I'm following an example of MVVM used in this great post about iOS architecture patterns and trying to adapt it to my app.
Here's my view controller:
class CalculatorViewController: UIViewController, UITextFieldDelegate, DismissalDelegate {
var viewModel: CalculatorViewModelProtocol! {
didSet {
self.viewModel.oneRepMaxDidChange = { [unowned self] viewModel in
self.oneRepMaxField.text = String(viewModel.oneRepMax!)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let viewModel = CalculatorViewModel() // use of unresolved identifier 'CalculatarViewModel'
self.viewModel = viewModel
liftNameButton.setTitle(viewModel.liftName, forState: .Normal)
weightLiftedField.text = String(viewModel.weightLifted)
repetitionsField.text = String(viewModel.repetitions)
units.text = viewModel.units
oneRepMaxField.text = String(viewModel.oneRepMax!)
// a bunch of formatting code and then I add a target to a button the user will press:
calculateButton.addTarget(self, action: #selector(onCalculateTapped), forControlEvents: UIControlEvents.TouchUpInside)
func onCalculateButtonTapped() {
if let weightLifted = weightLiftedField.text, let repetitions = repetitionsField.text {
// error: Argument passed to call that takes no arguments (except that it does)
viewModel!.calculateOneRepMax(weightLifted, repetitions: repetitions)
//weightPercentages = getPercentages(pureOneRepMax!)
} else {
return
}
and here's my view model and a view model protocol where the 'Type not allowed error' appears:
protocol CalculatorViewModelProtocol: class {
var liftName: String? { get }
var weightLifted: Double? { get }
var repetitions: Int? { get }
var oneRepMax: String? { get set }
var oneRepMaxDidChange: ((CalculatorViewModelProtocol) -> ())? { get set }
var units: String? { get }
var date: String? { get }
func calculateOneRepMax()
**// the 'Type not allowed here' error is here**
class CalculatorViewViewModel: CalculatorViewModelProtocol, LiftEventDataManagerDelegate {
let calculator = CalculatorBrain()
private let dataManager = LiftEventDataManager()
var liftName: String?
var weightLifted: String!
var repetitions: String!
var oneRepMax: String? {
didSet {
self.oneRepMaxDidChange?(self)
}
}
var units: String?
var date: String?
var oneRepMaxDidChange: ((CalculatorViewModelProtocol) -> ())?
#objc func calculateOneRepMax(weightLifted: String, repetitions: String) {
let result = calculator.calculateOneRepMax(Double(weightLifted)!, repetitions: UInt8(repetitions)!)
}
init() {
dataManager.requestData(withViewModel: self)
}
}
I've done a lot of searching but haven't found any answers that help.
You can not implement a class inside a protocol. Move your CalculatorViewModel to a separate file or at least outside the scope of the protocol