I'm having problems with an object that returns me in Realm, the strange thing is that if I printo console the object if I start it well but however if I try to access its value it tells me that it is empty.
The structure of the object is as follows:
class Favourite : Object {
var character : Character!
}
I create an object and add it to the DB
let fav = Favourite()
fav.character = character
FavouriteDao.sharedInstance.addFavourite(characterFavourite: fav)
Get all objects of favorite type
func getAllFavourites() -> Results {
return realm.objects(Favourite.self)
}
When I get the item and do a print
Favourite {
character = Character {
name = Spider-Man;
descriptionC = Bitten by a radioactive spider, high school student Peter Parker gained the speed, strength and powers of a spider. Adopting the name Spider-Man, Peter hoped to start a career using his new abilities. Taught that with great power comes great responsibility, Spidey has vowed to use his powers to help people.;
thumbnail = Thumbnail {
id = 815D93D0-C116-4267-978C-9E47C0074D0D;
path = http://i.annihil.us/u/prod/marvel/i/mg/3/50/526548a343e4b;
extensionImage = jpg;
};
};
If I try to access the character element it tells me that it is nil
Somebody manages to understand because if I make a print of the favorite object it shows me that there is inside a character object but nevertheless if I try to accede to it it says that it does not exist?
What you do is totally wrong from the very beginning. You should read the realm docs first. https://realm.io/docs/swift/latest/#getting-started
For example.
class Favourite : Object {
var character : Character!
}
is not something you should do in Realm.
Assuming your Character is well-defined, the code should be dynamic var character : Character? = nil at least.
Related
My app has data that looks like this.
class ShelfCollection: Object {
let shelves: List<Shelf>
}
class Shelf: Object {
let items: List<Item>
}
class Item: Object {
var name: String
let infos: List<String>
}
I'm trying to get all shelves in a shelf collection where any items match the query either by name or by an element in their infos list. From my understanding this predicate should be correct, but it crashes.
let wildQuery = "*" + query + "*"
shelfResults = shelfCollection.shelves.filter(
"SUBQUERY(items, $item, $item.name LIKE[c] %# OR SUBQUERY($item.infos, $info, info LIKE[c] %#).#count > 0).#count > 0",
wildQuery, wildQuery
)
It complies as a NSPredicate, but crashes when Realm is attempting to parse it, throwing me
'RLMException', reason: 'Object type '(null)' not managed by the Realm'
I suspect the nested subquery might be what fails, but I don't know enough about NSPredicate to be sure. Is this an acceptable query, and how can I make it.. work?
This is an answer and a solution but there's going to be a number of issues with the way the objects are structured which may cause other problems. It was difficult to create a matching dataset since many objects appear within other objects.
The issue:
Realm cannot currently filter on a List of primitives
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
so this Item property will not work for filtering:
let infos: List<String>
However, you can create another object that has a String property and filter on that object
class InfoClass: Object {
#objc dynamic var info_name = ""
}
and then the Item class looks like this
class Item: Object {
var name: String
let infos = List<InfoClass>()
}
and then you filter based on the InfoClass object, not it's string property. So you would have some objects
let info0 = InfoClass()
info0.info_name = "Info 0 name"
let info1 = InfoClass()
info1.info_name = "Info 1 name"
let info2 = InfoClass()
info2.info_name = "Info 2 name"
which are stored in the Item->infos list. Then the question
I'm trying to get all shelves in a shelf collection...
states you want to filter for a collection, c0 in this case, shelves whose items contain a particular info in their list. Lets say we want to get those shelves whose items have info2 in their list
//first get the info2 object that we want to filter for
guard let info2 = realm.objects(InfoClass.self).filter("info_name == 'Info 2 name'").first else {
print("info2 not found")
return
}
print("info2 found, continuing")
//get the c0 collection that we want to get the shelves for
if let c0 = realm.objects(ShelfCollection.self).filter("collection_name == 'c0'").first {
let shelfResults = c0.shelves.filter("ANY items.infoList == %#", info2)
for shelf in shelfResults {
print(shelf.shelf_name)
}
} else {
print("c0 not found")
}
I omitted filtering for the name property since you know how to do that already.
The issue here is the infos could appear in many items, and those items could appear in many shelf lists. So because of the depth of data, with my test data, it was hard to have the filter return discreet data - it would probably make more sense (to me) if I had example data to work with.
Either way, the answer works for this use case, but I am thinking another structure may be better but I don't know the full use case so hard to suggest that.
I've been learning iOS development for the past three weeks, I'm currently following a course on Udemy so far so good.
However I'm following one of the lectures whereby we build an Instagram Clone.
The instructor is using three arrays which are as follows:
var usernames = [""] // Stores all usernames
var userIds = [""] // Stores all Id's of the given usernames
var isFollowing = [false] // Stores where or not you're following that user
To me trying to keep track of what userId goes with what username using two arrays is basically an accident waiting to happen so I decided to set off and find a more feasible approach. I reverted back to my .Net days and decided to create a list so I went and created a class as follows:
class Users{
var Username : NSString = ""
var UserId : NSString = ""
var Following : Bool = false
}
Now inside my ViewController I make a call to Parse which returns me a list of users and I'm basically trying to loop through the response, and add them to the list class as shown here:
var t = [Users]() // After googling the web, this seems to be the syntax for a list declaration ?
let u = Users()
for object in users{
if let o = object as? PFUser {
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Now when I print this to the console I see the following:
ParseStarterProject_Swift.Users
As I have one user at present, however when I try to loop through T and display the username in the console it doesn't display anything.
for x in t {
print(x.Username)
}
Your basic intuition is correct, it's better to have an array of custom objects, not multiple arrays.
Regarding making it more Swifty, consider your Users type. You might want something like:
struct User {
let username: String
let userId: String
let following: Bool
}
Note,
property names should start with lowercase letter;
Users should probably be called User, as it represents a single user;
we don't generally initialize values to default values like that, but rather specify them in the initializer;
we probably use String not NSString;
if a property cannot change, you'd use let, not var;
properties begin with lower case letters;
Then you can do something like:
var t = [User]()
for object in users {
if let o = object as? PFUser {
t.append(User(username: o.username!, userId: o.objectId!, following: o.IsFollowing!)
}
}
print(t)
Clearly, with all of those ! forced unwrapping operators, you'd want to be confident that those fields were populated for all of those properties.
Using struct is nice because (a) it's a value type; (b) you get the initializer for free; and (c) you can just print them. If you really wanted User to be a reference type (a class), you'd do something like:
class User {
let username: String
let userId: String
let following: Bool
init(username: String, userId: String, following: Bool) {
self.username = username
self.userId = userId
self.following = following
}
}
And if you wanted to be able to just print them, you'd define it to conform to CustomStringConvertible:
extension User: CustomStringConvertible {
var description: String { return "<User; username = \(username); userId = \(userId); following = \(following)>" }
}
With the class, you can feel free to change that description computed property to show it in whatever format you want, but it illustrates the idea.
You are correct in considering that keeping track of what userId goes with what username using two arrays is dangerous, you in the correct direction with your approach.
First, I would just like to suggest that you use correct naming convention:
Classes should be singular (except in very specific cases).
Variable/property names should begin with lowercase.
This would mean that your user class should look like this:
class User {
var username : NSString = ""
var userId : NSString = ""
var following : Bool = false
}
I will keep your existing naming use for the next part. The main problem with your code is that the variable "u" is a object which you create only once and then modify it. You should be creating a new "Users" object for each user instead of modifying the original. If you don't do this you will just have an array with the same user multiple times. This is how your code would look now:
var t = [Users]()
for object in users {
if let o = object as? PFUser {
let u = Users()
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Next you mention that when you print to console you see the text: ParseStarterProject_Swift.Users, that is because Swift does not automatically print a pretty text with the content of your object. In order for it to print something more detailed, your "Users" object would need to implement the CustomStringConvertible. You can see a more detailed answer about that here: how-can-i-change-the-textual-representation-displayed-for-a-type-in-swif.
Lastly, you mention that when you loop trough "t" and display the username in the console it does not display anything. This is caused by one of two things:
Because there are no users being returned from parse, so the "t" array is actually empty. Try print(t.count) to see how many objects are in the array.
Because your "Users" object declares an empty string "" as the default username and the username is not being set correctly when getting the data from the parse. Which means that it IS actually printing something, just that it is an empty string. Try defining a different default value like var username : NSString = "Undefined" to see if it prints something.
Good luck learning swift!
I'm building a FF extension, and I'm processing some xhtml for myself in order to supporn subforms loading, so I have to identify the elements with l10n attributes defined and add them the string value. Because the l10n can't be shared from main code to content scripts (because isn't a simple JSON object), I managed the situation by getting the loaded keys values and defining an "localized array bundle", like this:
lStrings = ["step_title", ........ ];
for (var i = 0; i < lStrings.length; i++) {
bundle[lStrings[i]] = this.locale(lStrings[i]);
}
The thing is, I have to write here every entry in the .properties files... SO, do you know how to access this key values? I already tryed with .toString .toLocalString and inspecting the object, but can't find the way the object to be capable of returning all the key collection.
Do you have a better idea for improvement?
var yourStringBundle = Services.strings.createBundle('chrome://blah#jetpack/content/bootstrap.properties?' + Math.random()); /* Randomize URI to work around bug 719376 */
var props = yourStringBundle.getSimpleEnumeration();
// MDN says getSimpleEnumeration returns nsIPropertyElement // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIStringBundle#getSimpleEnumeration%28%29
while (props.hasMoreElements()) {
var prop = props.getNext();
// doing console.log(prop) says its an XPCWrappedObject but we see QueryInterface (QI), so let's try QI'ing to nsiPropertyElement
var propEl = prop.QueryInterface(Ci.nsIPropertyElement);
// doing console.log(propEl) shows the object has some fields that interest us
var key = propEl.key;
var str = propEl.value;
console.info(key, str); // there you go
}
See comments for learning. Nice quesiton. I learned more about QI from replying.
Can somebody give me an example of how to make inserting data into an F# record more flexible?
I often see examples using records like this:
type Employee = {mutable name:string; mutable id:string}
let data =
[{name = "Thomas";id = "000"};
{name = "Johny";id = "001"};
{name = "Lucky";id = "002"};
{name = "Don";id = "003"}
]
Can't we start with no data at all and insert the data into the record later?
(What I mean is without declaration of the value of the data like in the example, so for example: the program is running and asking us to insert the data)
Can we doing something like this with record?
If you're talking about specifying values of a record as they become available, then you need to make fields of the record option so that you can represent the fact that value is missing. I'll use immutable records, because this is more common in functional style:
type Employee = { Name:option<string>; ID:option<string> }
Now you can create a record with only ID and add name when the user enters it:
let empty = { Name = None; ID = Some 123 }
let name = // read name from user
let full = { empty with Name = name }
If you're talking about adding items to a list as they become available, then you have several options. The direct one is to write a recursive function that repeatedly reads record and builds a list:
let rec readData i records =
let name = // read name from user
if name <> "" then
// Create new record and add it to our list
let itm = { Name = name; ID = string i }
readData (i + 1) (itm::records)
else
// Return records that we collected so far in the right order
records |> List.rev
Alternatively, you can also use sequence expressions (see for example free Chapter 12 (PDF) of Real-World Functional Programming). If you user interaction involves waiting for events (e.g. mouse click), then you can still use this style, but you'd need to wrap everything in asynchronous workflow and use Async.AwaitEvent.
Are you saw you often saw an example like that?
I'd say that it is not very idiomatic in F# to use mutable records.
Immutability is a rather large subject
to explain in one answer here, but
briefly: immutability means that the
objects you create never change:
they stay the way they were at
creation. In the immutable world, when
you want to 'change' something, you
create a new one, and throw away the
old one.
Anyway, if I understand your question correctly, you are actually talking about mutating data, not the record. So, you could have:
let data = []
let data = {name = "Thomas";id = "000"} :: data
let data = {{name = "Johny";id = "001"} :: data
But in this case, you aren't really 'changing' data, you are just creating a new list each time and pointing data at it.
Its a little tricky to search for 'var:*' because most search engines wont find it.
I'm not clear exactly what var:* means, compared to say var:Object
I thought it would let me set arbitrary properties on an object like :
var x:* = myObject;
x.nonExistantProperty = "123";
but this gives me an error :
Property nonExistantProperty not found on x
What does * mean exactly?
Edit: I fixed the original var:* to the correct var x:*. Lost my internet connection
Expanding on the other answers, declaring something with type asterisk is exactly the same as leaving it untyped.
var x:* = {};
var y = {}; // equivalent
However, the question of whether you are allowed to assign non-existant properties to objects has nothing to do with the type of the reference, and is determined by whether or not the object is an instance of a dynamic class.
For example, since Object is dynamic and String is not:
var o:Object = {};
o.foo = 1; // fine
var a:* = o;
a.bar = 1; // again, fine
var s:String = "";
s.foo = 1; // compile-time error
var b:* = s;
b.bar = 1; // run-time error
Note how you can always assign new properties to the object, regardless of what kind of reference you use. Likewise, you can never assign new properties to the String, but if you use a typed reference then this will be caught by the compiler, and with an untyped reference the compiler doesn't know whether b is dynamic or not, so the error occurs at runtime.
Incidentally, doc reference on type-asterisk can be found here:
http://livedocs.adobe.com/labs/air/1/aslr/specialTypes.html#*
(The markup engine refuses to linkify that, because of the asterisk.)
It's a way of specifying an untyped variable so that you can basically assign any type to it. The code
var x:* = oneTypeObject;
creates the variable x then assigns the oneTypeObject variable to it. You can assign an entirely different type to it as well as follows:
var x:* = anotherTypeObject;
However, you still can't arbitrarily set or access properties; they have to exist in the underlying type (of either oneTypeObject or anotherTypeObject).
Both types may have identically named properties which means you can access or set that property in x without having to concern yourself with the underlying type.
It's the "untyped" type. It just means that the variable can be of any type. Basically the same effect as using this:
var x = myObject;
It means that the type is not specified and can be used with any type. However, you can't set random properties on it. It will behave like whatever type you set it to. The exact syntax is:
var x:*;
As they said before, it's untyped, so it may hold any kind of data. However, you cannot treat it as such in operations. For example, this is valid code:
var untyped:* = functionThatReturnsSomeValue();
But if you go one step farther, you have to watch out or you might get bitten:
var name:String = untyped.name;
Now, if the object returned by that function happens to be an object with a field with an id of "name," you are in the clear. However, unless you know for sure that this is the case, it's better to use typed objects. In this way, the compiler will alert you if you do something that might throw an error at runtime:
(elsewhere)
public class TypedObject()
{
public var name:String = "";
}
(and the code at hand)
var typed:TypedObject = functionThatReturnsTypedObject();
var name:String = typed.name;