So I have created a model class where I am using Alamofire to get data from an API. I want to display that data in the table to view so I was thinking of appending that model into an array in the VC and call it in the custom cell.
But I want to append that model into the array only if a key value matches a particular string.
However when I am using a simple if statement in the VC its giving me a fatal error saying bad instruction.
Code
Calling the Model
var notificationModel: NotificationModel!
var notification = [NotificationModel]()
viewDidLoad
if notificationModel.type == "meeting" {
self.notification.append(notificationModel)
}
Model
Class NotificationModel
var _type: String!
var type: String {
if _type == nil {
_type = "Err"
}
return _type
}
func downloadData() {
...
}
}
Here you have force unwrap NotificationModel. And you are trying to access type of a nil value.
Please try with the following fix.
var notificationModel: NotificationModel!
var notification = [NotificationModel]()
if notificationModel?.type == "meeting" {
notification.append(notificationModel!)
}
Hope this resolves the issue.
Related
I met a problem in realm-cocoa 2.8.0(in 2.7.0 it works good) which is when I want to save an object into the realm file, I saw an empty object with default value is saved into the realm rather than the object I created(even the primary key is different.)
Eg.
class XXXRealmObject: RLMObject {
#objc dynamic var id: String = UUID().uuidString.lowercased()
#objc dynamic var name: String = ""
#objc init(name: String) {
self.name = name
super.init()
}
#objc override init() {
super.init()
}
override class func primaryKey() -> String {
return "id"
}
}
let obj = XXXRealmObject(name: "jojo")
let realm = try! RLMRealm(configuration: .default())
try? realm.transaction {
*breakpoint*
realm.addOrUpdate(object)
}
I add a before realm.addOrUpdate(object) and print the object, it show correct object, but after realm.addOrUpdate(object) get executed, in realm file, I can only see an object
{
id: 169e6bc2-9b34-44ae-8ac3-70e6b9145adc,
name: ""
}
and the id is also different from what I saw in break point. It looks like Realm create an object rather use the object I passed in. I am asking for some help here.
So what will cause realm create an empty object(maybe default value?) rather than save the object I passed. I just want to get some possible reasons here.
I think I got it, in my project, we have a category for NSObject which include a method called objectForKey, and in Realm's src, when we read value from RLMObject, we check if it can response to objectForKey, normally it should return false and keep executing following code to get the real value, but in my project, the code will return nil because it's not a dictionary.
So will close this
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. :)
I have stop which have many directions. I've managed to do it using function 'linkingObjects' but since last update it's deprecated and I should use object 'LinkingObjects'.
Stop
class Stop: Object {
dynamic var name:String = ""
var directions = List<Direction>()
override static func primaryKey() -> String? {
return "name"
}
}
Old Direction
class Direction: Object {
dynamic var tag:String = ""
var stop:Stop? {
return linkingObjects(Stop.self, forProperty: "directions").first
}
}
When I apply my previous approach to new object then I always get nil
New Direction with nil returned by LinkingObjects
class Direction: Object {
dynamic var tag:String = ""
let stop = LinkingObjects(fromType: Stop.self, property: "directions").first //always return nil
}
But here I'm getting array with one element. So it works as it should.
New Direction with nil returned by LinkingObjects
class Direction: Object {
dynamic var tag:String = ""
let stops = LinkingObjects(fromType: Stop.self, property: "directions")
}
Question
Is there other way to use 'LinkingObjects' rather than this last example because using in every time 'direction.stop.first?.name' instead of 'direction.stop?.stop'?
Of course, I could use in 'direction' function that will take always pick first element in 'stops' but maybe I don't have to.
UPDATE
In meanwhile I’m using this. This isn’t ideal solution, but works.
private let stops:LinkingObjects<Stop> = LinkingObjects(fromType: Stop.self, property: "directions")
var stop:Stop? {
return self.stops.first
}
The solution you came up with yourself for now is the best what you could do.
We discussed whether we should expose any other API for singular backlinks, but as there is no way to enforce their multiplicity on the data storage layer, it didn't made sense so far. In addition, you would still need a wrapper object, so that we can propagate updates. For that reason a unified way to retrieve the value via LinkedObjects seemed to be the way so far.
What about specifiying the relation the other way around?
Specify the connection on the 'one'-side
Do a query in the getter on the 'many'-side:
So it should read like this:
class Child: Object {
dynamic var name:String = ""
dynamic var parent:Parent? = nil
}
class Parent: Object {
dynamic var name:String = ""
var children:Results<Child>? {
return realm?.objects(Child.self).filter(NSPredicate(format: "parent == %#", self))
}
}
I'm trying to understand some code in a project I'm working on. I have an array property of strings:
var names: [String]!
func findName(name: String?) -> [Name]? {
if name != nil {
return nameManager.namesForSearchString(name)?.filter({self.names.contains($0.name)})
} else {
return nameManager.allNames.filter({self.names.contains($0.name)}) //<-what get's returned here?
}
}
What I don't understand is if the name is nil, what happens when .contains is called, and with that, what happens when .filter gets called? This is implemented in a Favorites class, and I need to call this function to return all favorites if a button is tapped, so what would I pass to this function to ensure that all the contents of Names: [Name] are returned?
On a lower level, I want to understand how .contains and .filter work and what gets returned if nil is passed to them.
Another version of the same method from a different commit (that I also did not write) is this:
func findFavorites(name: String?) -> [Station]? {
if name != nil {
return nameManager.namesForSearchString(name)!.filter({contains(self.names, $0.objectId)})
} else {
return nameManager.allNames.filter({contains(self.names, $0.objectId)})
}
}
I don't want to post a non-answer, but I do want this to be properly formatted so I guess a comment won't do. This might help you understand what's going on, and what happens with filter/contains. If you have any more questions, let me know, and I'll answer the question. If I'm completely off-base, let me know as well!
// I don't know why this is implicitely unwrapped, as a nil in this Array crashes Playground execution
var localNames: [String!] = ["Troy", "Bob", "Donald"]
// I'm just modelling what I know about NameManager
struct NameManager {
var allNames = [Name(name: "Bob"), Name(name: "Liz"), Name(name: "Anastasia")]
}
// I also assume the `name` in Name is a non-optional.
struct Name {
var name: String = "some name"
}
var nameManager = NameManager()
func findName(name: String?) -> [Name]? {
// Case where `name` is non-nil is excluded for demonstration purposes
// I have expanded all the closure short-hands so we always see what we're doing.
let allNames = nameManager.allNames
// namesMatchingName is of type [Name], that we get by applying a filter.
// `filter` works on a predicate basis: it goes through each element, one at a time,
// and checks if it meets the "predicate", that is, a boolean
// condition that returns true or false. If it DOES meet the criteria, it will be included in
let namesMatchingName = allNames.filter { (currentName) -> Bool in
// Now we're inside the filter-predicate. What we do here is check if the `currentName`
// is in `localNames`.
let namesHasCurrentName = localNames.contains(currentName.name)
// If the name IS in `localNames` we return true to the filter,
// which means it will be included in the final array, `namesMatchingName`.
return namesHasCurrentName
}
// So now we have all the names that appear in both `nameManager.allNames` and `localNames`
return namesMatchingName
}
findName(nil) // returns [{name: "Bob"}]
I ran into a problem when I was trying to "translate" some Objective-C code to Swift. I define Garage and Car in CoreData. Garage has a relationship to Car called cars. I have a masterviewcontroller to display "Garage" class and detailviewcontroller to display a NSArray of "car" class. Here is my code in Objective-C. I want to let cars = allobjects when it is nil; otherwise just return it.
#property (nonatomic, strong) NSArray* cars;
- (NSArray*) cars {
if (_cars == nil) {
_cars = self.garage.cars.allObjects;
}
return _cars;
}
However, in Swift, it does not have a underscore instance for property, and I cannot let cars == nil since "==" cannot be applied to operands of type [Car]. I tried to use the following code, but it gave me two errors: "attempting to access 'cars' within its own getter" and "cannot assign a value of type '[AnyObject]?' to a value of type '[Car]'"
var garage : Garage?
var cars : [Car] {
if let a = cars {
get {
cars = self.garage?.cars.allObjects
}
}
Any help is appreciated.
UPDATE/////////////////////////////////////////////
Here is the method I used to solve my problem.
private var _cars: [Car]?
var cars: [Car]?
{
get {
if _cars == nil {
_cars = self.garage?.cars.allObjects as? [Car]
}
return _cars
}
set {
_cars = cars
}
}
Thank you for all the help.
I might suggest just having a computed property:
var cars : [Car]? {
return garage?.cars.allObjects
}
And, remember to make cars variable and optional array.
Looks like you need a lazy property:
lazy var cars: [Car]? = self.garage?.cars.allObjects
Yes, you need a lazy var.
Your code will be (Swift 2.3):
lazy var cars: [Car]? = { [unowned self] in
return self.garage?.cars.allObjects
}()
The trick are the { and the [unowned self]
Lazy var in sintax instance the value just one time and keep it, maybe it's what you want.
Maybe you just need a wrapper to get each time the value of self.garage?.cars.allObjects in which case you need a computed var, not a lazy var with a static value
var cars: [Car]? {
get {
return self.garage?.cars.allObjects
}
}
In Swift, you would generally replace properties with variables (var); if a property can be set in you init method and stays unchanged after that, you would use (let).
In your case, you have your own "get" method. In Swift, when you have a getter or setter method, there is no backing variable. So what you do, you create one. Typically as a private variable with a leading underscore.
You should think about how you reset the variable to nil. Having a setter is not a good idea, because you only want to allow setting the variable to nil and nothing else. If it is only set to nil inside the class itself, you can set the variable with underscore to nil. Otherwise you might add a function like "resetCars".
And note that if you have a "get" method, but none of "set", "willSet" and "didSet", you can just write the set code without any getter. So a typical usage would be:
private var _cars: [Car]?
func resetCars () -> Void { _cars = nil } // To allow resetting outside the class
var cars: [Car] {
if _cars == nil {
_cars = cleverlyCalculatedNonNilValue()
}
return _cars
}