RealmSwift initializer: self used before super.init call - ios

import RealmSwift
import Realm
public class Card : Object {
var username : String
var firstName : String
var lastName : String
init?(dictionary: [String:Any]?) {
guard let dictionary = dictionary , let username = dictionary["username"] as? String else { return else}
self.username = username
self.firstName = firstName
self.lastName = lastName
}
required public init() {
fatalError("init() has not been implemented")
}
required public init( realm: RLMRealm, schema: RLMObjectSchema) {
fatalError("init(realm:schema:) has not been implemented")
}
required public init( value: Any, schema: RLMSchema) {
fatalError("init(value:schema:) has not been implemented")
}
}
I get:
'self' used before super.init call
I had my class working properly. After adding RealmSwift i'm getting those errors. If I add super.init() it complains:
Property 'self.username' not initialized at super.init call

Because your properties are String.
From the apple docs...
Setting Initial Values for Stored Properties
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
You can set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition. These actions are described in the following sections.
You have two options:
1)
var username : String = ""
var firstName : String = ""
var lastName : String = ""
2)
var username : String?
var firstName : String?
var lastName : String?

There's a couple of things going on here.
First and foremost, when adding custom initializers to subclasses of Object, they must be declared as convenience initializers. It's not possible to correctly implement Object's required initializer from a subclass, and using a convenience initializer removes the need to try and do this. It also means you'll instead delegate to self.init() rather than super.init() from within your custom initializer.
Secondly, stored properties must have an initial value. Without the initial value the Swift compiler will not synthesize initializers for your class (in this case, a version of init() that calls through to the base class).
Finally, as I mentioned elsewhere, properties of type String must be declared using Swift's dynamic modifier to allow Realm to intercept get / set operations on them.
By following these guidelines you'll end up with something like so:
public class Card : Object {
dynamic var username: String = ""
dynamic var firstName: String = ""
dynamic var lastName: String = ""
convenience init?(dictionary: [String:Any]?) {
guard let dictionary = dictionary,
let username = dictionary["username"] as? String,
let firstName = dictionary["firstName"] as? String,
let lastName = dictionary["lastName"] as? String
else { return nil }
self.init()
self.username = username
self.firstName = firstName
self.lastName = lastName
}
}

Related

How to insert different type of element in array

I have a class that has a reference to another class. A student class has a variable that has a reference to module info. I want to insert the moduleId into an array but the struct is made up of different types of objects and is of type moduleRef. I would like to get the moduleId which is of type string. How can I grab this moduleId element form ModuleRef.
class Student {
var firstName: String
var lastName: String
var modulesRefs: [ModuleRef]
var fullName: String {
return self.firstName + " " + self.lastName
}
init(firstName: String, lastName: String, modulesRefs: [ModuleRef]) {
self.firstName = firstName
self.lastName = lastName
self.modulesRefs = modulesRefs
}
func addModuleId(_ moduleId: String) {
self.modulesRefs.insert(moduleId, at: 0)
}
func removeModuleId(_ moduleId: String) {
self.modulesRefs = self.modulesRefs.filter { $0.moduleId != moduleId }
}
}
class ModuleRef {
var moduleName: String
var moduleId: String
var moduleStartdate: Int
init(moduleName: String, moduleId: String, moduleStartdate: Int) {
self.moduleName = moduleName
self.moduleId = moduleId
self.moduleStartdate = moduleStartdate
}
}
Cannot convert value of type 'String' to expected argument type 'ModuleRef'
You can insert the values of Type ModuleRef into that array. So i may modify the functions as:
func addModuleId(_ moduleId: String) {
let module = ModuleRef(moduleName: "", moduleId: moduleId, moduleStartdate: 0)
self.modulesRefs.insert(module, at: 0)
}
func removeModuleId(_ moduleId: String) {
modulesRefs.removeAll(where: { $0.moduleId == moduleId })
}
NB: If you don't need moduleName & moduleStartdate all time, you can mark it as Optional.
In your code you have made class ModuleRef { ... } values compulsory and you are just passing a single value moduleId and you are adding it to an Array of ModuleRef which is totally wrong approach. So, based on what you have said here is the updated code of your code:
class Student {
var firstName: String
var lastName: String
var modulesRefs: [ModuleRef]
var fullName: String {
return self.firstName + " " + self.lastName
}
init(firstName: String, lastName: String, modulesRefs: [ModuleRef]) {
self.firstName = firstName
self.lastName = lastName
self.modulesRefs = modulesRefs
}
func addModuleId(_ moduleId: String) {
// This will be your updated intializer where you can pass any value you want and other will be taken as default value
let moduleRef = ModuleRef(moduleId: moduleId)
self.modulesRefs.insert(moduleRef, at: 0)
}
func removeModuleId(_ moduleId: String) {
self.modulesRefs = self.modulesRefs.filter { $0.moduleId != moduleId }
}
}
class ModuleRef {
var moduleName: String
var moduleId: String
var moduleStartdate: Int
/// Here you can provide all the default values which you don't want to pass
init(moduleName: String = "", moduleId: String = "", moduleStartdate: Int = 0) {
self.moduleName = moduleName
self.moduleId = moduleId
self.moduleStartdate = moduleStartdate
}
}
Besides, you can also pass the nil value for all making the variable as optional. Let me know, if you have any confusion!
You cannot insert a ModuleRef just by passing a String.
You need to create an instance for example adding the full name of the student and the UNIX timestamp of the current date
func addModuleId(_ moduleId: String) {
let moduleRef = ModuleRef(moduleName: self.fullName,
moduleId: moduleId,
moduleStartDate: Int(Date().timeIntervalSince1970))
self.modulesRefs.insert(moduleRef, at: 0)
}
And to remove an object this is more efficient, however the class must conform to Equatable
func removeModuleId(_ moduleId: String) {
guard let index = self.modulesRefs.firstIndex(where: {$0.moduleId == moduleId}) else { return }
self.modulesRefs.remove(at: index)
}
I'd even declare moduleStartDate as Date.
You need to updated your Array type to Any as follows:
var modulesRefs: [Any]
While access value from array,
if let moduleRef = modulesRefs[index] as? ModuleRef {
} else if let str = modulesRefs[index] as? String {
}
What you're trying to do, actually means that there's a flaw in your design.
If you need the functionality to call student.addModuleId(moduleId) that means, that Student should not hold an array of ModuleRef. (Or at least have a mechanism to fetch the whole ModuleRef by it's id, and then insert that ModuleRef inside the array)
That being said, After figuring out your design to which will it be: modules: [ModuleRef] or moduleIds: [String], there are still somethings that you can get use out of:
Equatable protocol. If you make ModuleRef to conform to Equatable protocol, and check its equality with id, then Swift will give you access to remove(element:_) method, and you no longer need the removeModuleId() method.
Or if all ModuleIds are unique, you can implement Hashable protocol as well, and use Set<ModuleRef> instead of an array to have O(1) remove and insert methods. (right now, remove function, takes O(n) time)
You need to make an array of Any type..
var modulesRefs: [Any] = []
You can add element in array like this...
modulesRefs.append("Test string")
let obj = ModuleRef(...)
modulesRefs.append(obj)
And when you access an element you need to user either if let or guard let to check its type with safety.
For eg.
for obj in modulesRefs {
if let strType = obj as? String {
print("\(obj) is string type")
} else if let moduleRefObj = obj as? ModuleRef {
print("\(obj) is ModuleRef type")
}

How to access model class values in another controller using singleton in swift?

I have created a class with some variables and have not initialized with any default value, all variables should be assigned when web service call occurs. So I have initialized the instance of the class and assigned the value to the variables.
Here I want to access these values to all class file throughout the project. Is it possible? I do not want to use any saving methods like core data and user defaults also codable local storage.
Please help me out with this? We tried to access the model class value in another view controller. But we get a nil value. Thanks in Advance.
//MARK: Shared Instance
static let sharedInstance = Singleton()
//MARK: Local Variable
var emptyStringArray : [String]? = nil
var completed : Bool!
var id : Int?
var title : String?
var userId : Int?
//MARK: Init Array
private init() {
}
init(Fromarray dictionary : [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
finally called in
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let BoolValue = Singleton.sharedInstance.completed
print(BoolValue)
}
This is a very poor architectural decision.
Among the many the first problem here is that results of API calls are always asynchronous so you can't tell if your class have been properly initialized at the moment you use it's properties.
You create too much ambiguity by creating forced unwrapped optional that depends on network call. What if API call fails? What if internet connection is slow, how would you predict that your singleton is "fine" at this moment? For most of the cases you will be accessing nil and it will crash your app.
I'd suggest more reading on Singleton pattern (if it's necessary) and also on architectural patterns in iOS.
use different function to load data and call that after WebCall
func loadData(Fromarray dictionary : [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
call after web call completion
Singleton.sharedInstance.loadData(FromArray : YourDictoinary)
and acess anywhre throught project
let title = Singleton.sharedInstance.title
You need to keep a single object of your singleton class.
static let sharedInstance = Singleton()
It will create a object of your singleton class.
private init() { } or,
init(fromarray dictionary: [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
It will create another object of your singleton class, which will be differ form above.
If you need to access the data from your singleton class. Create your class like:
class Singleton {
static let sharedInstance = Singleton()
var emptyStringArray: [String]?
var completed: Bool?
var id: Int?
var title: String?
var userId: Int?
func initializeData(_ dictionary: [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
}
And use it like:
override func viewDidLoad() {
super.viewDidLoad()
// Initialize with data
Singleton.sharedInstance.initializeData(["completed": true, "id": 123, "title": "iOS Title", "userId": 572])
// Access data
let boolData = Singleton.sharedInstance.completed
print(boolData)
}

Cannot store lists - Realm Swift

I am trying to create a one to many relationship between medication table and side effects table. RealmObject is a custom class created from Object class.
Model definitions:
class Medication: RealmObject {
#objc dynamic var name : String?
#objc dynamic var form : String?
#objc dynamic var id : String?
let medToSideEffect = List<SideEffect>()
override class func primaryKey() -> String? {
return "id"
}
}
class SideEffect: RealmObject {
#objc dynamic var masterSideEffectId : String = ""
#objc dynamic var entityType : String = ""
#objc dynamic var entityId : String = ""
#objc dynamic var sideEffect : String = ""
#objc dynamic var id : String = ""
#objc dynamic var uniqueId : String = ""
override class func primaryKey() -> String? {
return "uniqueId"
}
}
Test code:
let medicationItem = Medication(dict: medication)
let sideEffectItems = List<SideEffect>()
for sideEffect in sideEffectList {
let sideEffectItem = SideEffect()
sideEffectItem.id = self.getMongoId()
sideEffectItem.entityType = "Medicine"
sideEffectItem.entityId = medicationItem.id!
sideEffectItem.sideEffect = (sideEffect as? String)!
sideEffectItem.uniqueId = "\(medicationItem.id!)_\(((sideEffect as? NSString)?.replacingOccurrences(of: " ", with: "_"))!)"
sideEffectItems.append(sideEffectItem)
medicationItem.medToSideEffect.append(sideEffectItem)
}
After this process, when i print the medicationItem, Output is this
Medication {
name = Paracetomol 650mg;
form = tab;
id = 5af96e79efb27f6bd5c25a66;
}
The side effects were supposed to be added to the medication object. but that is not the case anymore. It was working fine until the latest update Realm 3.11.0.
Please let me know, if the code is incorrect.
Maybe because yYou declare your list as 'immutable', and maybe because your list is a struct and therefore a copy is returned.
let medToSideEffect = List<SideEffect>()
And so it cannot be changed. Try (warning untested code)
public private(set) var medToSideEffect = List<SideEffect>()
and add a func
public func append(sideEffect: SideEffect) {
self.medToSideEffect.append(sideEffect)
}
The 'public private (set)' bit means that the list is visible externally but can only be modified by the owning class instance.
EDIT:
https://stackoverflow.com/a/52704564/6700116
Found the solution.
Go to your target Build Settings and set Reflection Metadata Level flag to All
The issue is discussed here.
https://github.com/realm/realm-cocoa/issues/5944#issuecomment-426948127

How to debug "Use of unresolved identifier" in Swift?

I'm learning about Xcode and using tutorials. I'm a beginner.
I'm looking at this code and have no clue why this is coming up with the following error:
Use of unresolved identifier 'USER_REF'
but in the code I'm pretty sure I reference the USER - is that right?
class DataService {
static let dataService = DataService()
fileprivate var _BASE_REF = Database.database().reference()
fileprivate var _USER_REF = Database.database().reference()
fileprivate var _USERPOST_REF = Database.database().reference()
var BASE_REF: DatabaseReference {
return _BASE_REF
}
var USER_REF: DatabaseReference {
return _USER_REF
}
var CURRENT_USER_REF: DatabaseReference {
let userID = UserDefaults.standard.value(forKey: "uid") as! String
let currentUser = Database.database().reference().child(byAppendingPath: "user").child(byAppendingPath: userID)
return currentUser
}
var USERPOST_REF: DatabaseReference {
return _USERPOST_REF
}
}
func createNewAccount(uid: String, user: Dictionary<String, String>) {
// A User is born?
USER_REF.child(byAppendingPath: uid).setValue(user)
}
func createNewUserPost(userpost: Dictionary<String, AnyObject>) {
// Save the Post
// USERPOST_REF is the parent of the new USERPOST: "userposts".
// childByAutoId() saves the userpost and gives it its own ID.
let firebaseNewUserPost = USERPOST_REF.childByAutoId()
// setValue() saves to Firebase.
firebaseNewUserPost?.setValue(userpost)
}
This is a perfect lesson in why indentation is important. There's a second } after the end of USERPOST_REF: DatabaseReference, which ends the scope of the class DataService.
As a result, createNewAccount(uid:user:) and createNewUserPost(userpost:) are standalone functions without access to any instance members of DatabaseReference.
A few points of improvement:
Swift convention for all properties, instance, class or static, is to use lowerCamelCase.
Unlike other languages (Java and Ruby are the main offenders here that I know of), there is no point making an instance variable with a public getter (computed property), such as filprivate _x: Int, with a public var x: Int. Namely, because _x is not an instance variable. Swift doesn't actually let you make instance variables. They're generated for you when you create properties. What you're doing is writing a property that accesses a property you declared, to access an instance variable the compiler synthesized. There's no need for this. Just make the property's setter scoped to file private.
The conventional name of a singleton in Swift is shared, default or main. Try to stick with one of those. Additionally, in order for you to truly have a singleton, you need to restrict access to the initializer, by declaring it with private access.
Here is what I would recommend:
class DataService {
static let shared = DataService()
private init() {}
public fileprivate(set) var baseDB = Database.database().reference()
public fileprivate(set) var userDB = Database.database().reference()
public fileprivate(set) var userPostDB = Database.database().reference()
var currentUser: DatabaseReference {
let userID = UserDefaults.standard.value(forKey: "uid") as! String
return Database.database()
.reference()
.child(byAppendingPath: "user")
.child(byAppendingPath: userID)
}
func createNewAccount(uid: String, user: Dictionary<String, String>) {
// A User is born?
userDB.child(byAppendingPath: uid).setValue(user)
}
func createNewUserPost(userpost: Dictionary<String, AnyObject>) {
// Save the Post
// userPostDB is the parent of the new USERPOST: "userposts".
// childByAutoId() saves the userpost and gives it its own ID.
let firebaseNewUserPost = userPostDB.childByAutoId()
// setValue() saves to Firebase.
firebaseNewUserPost?.setValue(userpost)
}
}

Default initialiser in protocol requires unwanted mutable properties

In Swift 3: Imagine you want your models to be value types (struct) through out your app. But you would also like persistence of said models using Core Data/Realm, which requires you to create classes. Then you can convert your structs to classes and vice verse using JSON (which would require structs and classes to both support JSON deserialization and serialization).
Wouldn't it be neat if you don't have to write JSON deserialization (and analogously for serialization, but I'm focusing on deserialization here) in two places, but use put deserialization in a protocol, that both your struct and class uses.
Using structs we want our JSON model to have immutable fields, thus all properites being let constants. But using a protocol implementation of the deserialization does not allow for this AFAIK.
The code example below works, but it is ugly, because of all unwanted requirements (UR) marked in comments in the code.
struct JSON {
let json: [String: Any]
func value<Value>(_ key: String) throws -> Value {
guard let value = json[key] as? Value else { throw NSError() }
return value
}
}
protocol JSONDeserializable {
init(json: JSON) throws
}
protocol UserModel: JSONDeserializable {
var username: String { get set } // Unwanted requirement (UR) #1: property needs "set" so that it can be initialized within protocol
init() // UR2: needs empty init, because of default implementation of `init(json: JSON)` in `extension UserModel`
}
extension UserModel {
init(json: JSON) throws {
self.init() // UR3: Needs to call this otherwise compilation error: `'self' used before chaining to another self.init requirement`
username = try json.value("username")
}
}
struct UserStruct: UserModel {
// UR4: property cannot be `let`, beause of `set` in protocol.
var username: String = "" // UR5: Property have to have default value because of it being a empty init
init() {}
}
final class UserClass: NSObject, UserModel {
// UR6: analogue with UR4
var username: String = "" // UR7: analogue with UR5
}
let json: JSON = JSON(json: ["username": "Sajjon"])
let u1 = try UserStruct(json: json)
let u2 = try UserClass(json: json)
print(u1.username) // prints "Sajjon"
print(u2.username) // prints "Sajjon"
Is there another way of achieving this, with a lower amount of unwanted requirements? Or an optimal solution with zero UR? 🙄
Thanks to what #hamish pointed out, the best solution would be (where struct JSON and protocol JSONDeserializable remains the same as in the question). This is not a perfect solution since you have to implement the initializer of the class. The neat part is that you don't have to implement any initializer for the struct, since it has one implicitly.
protocol UserModel: JSONDeserializable {
var username: String { get }
var firstname: String { get }
var country: String { get }
init(
username: String,
firstname: String,
country: String
)
}
extension UserModel {
init(json: JSON) throws {
self.init(
username: try json.value("username"),
firstname: try json.value("firstname"),
country: try json.value("country")
)
}
}
struct UserStruct: UserModel {
let username: String
let firstname: String
let country: String
// struct has default initializer
}
final class UserClass: UserModel {
let username: String
let firstname: String
let country: String
init(
username: String,
firstname: String,
country: String
) {
self.username = username
self.firstname = firstname
self.country = country
}
}
let json: JSON = JSON(json: [
"username": "Sajjon",
"firstname": "Alexander",
"country": "Sweden"
])
let u1 = try UserStruct(json: json)
let u2 = try UserClass(json: json)
print(u1.username) // prints "Sajjon"
print(u2.username) // prints "Sajjon"

Resources