I have a Message/RLMObject model that has a NSString *jabberID property/row and I want to retrieve every unique value inside that row.
In other word, I want to retrieve non-repeated jabberID values from my Message model. Can anyone help out figuring this?
The way I use to do with coredata was using returnsDistinctResults setting on the NSFetchRequest.
Functional programming approach since Swift has it, and Realm lazy loads; Not as easy/available a solution in Objective-C but for Swift at least:
Swift
let distinctTypes = reduce(Realm().objects(User), []) { $0 + (!contains($0, $1.type) ? [$1.type] : [] ) }
UPDATED:
Swift reduce is kind of a performance intensive, allocating a bunch of intermediate array's, instead the following should be much better performance wise, but must be explicitly cast
let distinctTypes = Array(Set(Realm().objects(User).valueForKey("type") as! [String]))
I found out Realm doesn't fully support distinct queries yet. The good news is I also found a workaround for it, on this github issue.
Objective-c
RLMResults *messages = [Message allObjects];
NSMutableArray *uniqueIDs = [[NSMutableArray alloc] init];
NSMutableArray *uniqueMessages = [[NSMutableArray alloc] init];
for (Message *msg in messages) {
NSString *jabberID = msg.jabberID;
Message *uniqueMSG = (Message *)msg;
if (![uniqueIDs containsObject:jabberID]) {
[uniqueMessages addObject:uniqueMSG];
[uniqueIDs addObject:jabberID];
}
}
Swift 3.0
let realm = try! Realm()
let distinctIDs = Set(realm.objects(Message.self).value(forKey: "jabberID") as! [String])
var distinctMessages = [Message]()
for jabberID in distinctIDs {
if let message = realm.objects(Message.self).filter("jabberID = '\(jabberID)'").first {
distinctMessages.append(message)
}
}
Related
let sessions = TWTRTwitter.sharedInstance().sessionStore.existingUserSessions()
supposedly returns an array of TWTRSessions.
However the type of the elements is untyped (Any), and casting to TWTRSession using if let authsession = any as? TWTRSession fails. Also, force casting crashes obviously.
let authsession = any as! TWTRSession // crashes
Crash error:
Could not cast value of type 'TWTRSession' (0x103fbe370) to 'TWTRSession' (0x103cf5cc8).
That's a very interesting error, don't you think? TWTRSession matches, but sure, the hex values do not.
Oh, this worked up until before 3.3.0. No release notes or migration notes from the folks at Twitter. Things just silently stopped working.
Related: TWTRTwitter sessionStore now returns TWTRAuthSession: so how does one access the userName property now?
I ended up creating a C function to recreate the array and call it from my Swift file:
NSArray<TWTRSession *> *existingUserSessions(TWTRSessionStore *store) {
NSArray<TWTRSession *> *existingSessions = store.existingUserSessions;
NSUInteger count = existingSessions.count;
NSMutableArray<TWTRSession *> *sessions = [NSMutableArray arrayWithCapacity:count];
for (TWTRSession *session in existingSessions) {
[sessions addObject:session];
}
return [sessions copy];
}
I have database of about 10.000 entities and dynamic NSArray of 50 NSString elements. Would like to check if each of those elements exist in database and create new array of ones that exist. I don't need to return whole entities, just those NSString titles ( which are the same as in NSString array)
NSPredicate should compare entity.title to NSString element with EXACT match.
What is the best and processor/memory efficient way to do it?
I think that you should use the 'in' operation in your predicate to get your result. This lets you leverage the database to perform the comparison, instead of bringing back all of the 10,000 records to compare yourself. If you take this approach, your code could look like this:
// Assuming that arrayName is your existing array of values to match, that
// EntityName is the object in CoreData that you’re looking at, and context
// is your moc
var newArray: [String] = []
let fetchRequest = NSFetchRequest<EntityName>(entityName: "EntityName")
fetchRequest.predicate = NSPredicate(format: "title in %#", arrayName)
do {
result = try context.fetch(fetchRequest)
for element in result {
newArray.append(element.title)
}
} catch {
… manage any errors …
}
Note - I'm targeting Swift 3.0 compatible code - not sure if swift was what you're after.
I'm getting data from a MySQL-database in JSON-format. In an Objective-C file the data gets modified and put into an NSMutableArray ("_data"). By the function "itemsDownloaded" the delegate gets notified as soon the download from the database is finished and receives the "_data"-array.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Create an array to store the data
NSMutableArray *_data = [[NSMutableArray alloc] init];
// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingAllowFragments error:&error];
// Loop through Json objects, create question objects and add them to our questions array
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
// Create a new data object and set its props to JsonElement properties
Data *newData = [[Data alloc] init];
newData.sozialversicherungsnummer = jsonElement[#"Sozialversicherungsnummer"];
newData.messzeitpunkt = jsonElement[#"Messzeitpunkt"];
newData.puls = jsonElement[#"Puls"];
newData.sauerstoffgehalt = jsonElement[#"Sauerstoffgehalt"];
// Add this question to the locations array
[_data addObject:newData];
}
// Ready to notify delegate that data is ready and pass back items
if (self.delegate)
{
[self.delegate itemsDownloaded:_data];
}
}
My aim is to access the properties "sozialversicherungsnummer", "messzeitpunkt", "puls" and "sauerstoffsättigung" of "newData" (in the above file). The class "Data" defines these four properties.
Now I want to display these properties inside of a chart in a swift file. For example I want to display "messzeitpunkt" on the x-axis and "puls" on the y-axis. I know how to handle the chart but my problem is that I don't know how to get access to the properties inside of the swift file.
If I write these lines into my swift file:
var data: NSArray = [];
func itemsDownloaded(items: [AnyObject]!) {
data = items
print("\(data)")
}
I get this on my output:
(
"<Data: 0x7ca5ff60>",
"<Data: 0x7ca5dab0>",
"<Data: 0x7be497e0>",
"<Data: 0x7ca42c00>"
)
Can somebody please help me?
The problem is that you don't want an NSArray. Swift doesn't know what's inside an NSArray. You want a Swift array, namely a [Data]. That way, Swift knows that each item is a Data, and you can access its properties.
Your output is:
(
"<Data: 0x7ca5ff60>",
"<Data: 0x7ca5dab0>",
"<Data: 0x7be497e0>",
"<Data: 0x7ca42c00>"
)
And that is exactly what you want and expect! You have an array of four Data objects. The only problem is that you have forgotten to tell Swift about this. You need to type the array as a [Data] or cast it to a [Data].
For example, where you are now saying:
func itemsDownloaded(items: [AnyObject]!) {
data = items
print("\(data)")
}
Try saying this:
func itemsDownloaded(items: [AnyObject]!) {
let datas = items as! [Data]
datas.forEach {print($0.messzeitpunkt)}
}
That is legal, because now you have told Swift what is in the array. And you will see that your data is there, exactly as you intend.
I'm currently building an food tracker iOS app (using swift 2) and I would like to have a database with all the foods (and their info) stored in the app and accessible.
The idea is that when some add a 'ice cream' to their meal, his calories/sugar/fat 'counters' increase by the respective nutritional value of the ice cream. (so that this data can be processed later on)
I have found a database of food in what seems like JSON format (see bellow) but I have no idea how to process all this data with swift so that I could access the number of calories in a specific ingredient for example.
So far I tried this:
let url = NSURL(string: "myURL")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
} else {
let jsonResult = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
print(jsonResult)
}
})
task.resume()
}
It allows me to process the JSON format into a dictionary that I can access but what I need (I think) would be maybe a dictionary of arrays and I can't manage to make it work with the JSON format that I have bellow.
[
{
"Description": "Juice",
"Energy(kcal)Per 100 g": 29,
},
{
"Description": "Alcoholic beverage, daiquiri, canned",
"Energy(kcal)Per 100 g": 125,
}
...
]
I admit my question wasn't quite clear at first (I'm really new at this I apologize) but I actually tried to research it on Stackoverflow before posting, but I haven't find something that works for my case. Sorry again and many thank you for taking the time to still answer it :)
Have a look into NSJSONSerialization. That is what you get for free once installed xcode and the SDK. And it is not that bad actually.
This is Ray's approach to Swifty Json:
http://www.raywenderlich.com/82706/working-with-json-in-swift-tutorial
This is what you find when you use the search. You will have to "translate" it to swift though.
How do I parse JSON with Objective-C?
You may want to look at RestKit for some more convenient way of dealing with JSON sources.
Give it a try. And when you run into concrete problems, then get back to SO.
Just give it a try
var arrDicts: [Dictionary<String, AnyObject>] = []
arrDicts = try! NSJSONSerialization.JSONObjectWithData(dataFromService!, options: NSJSONReadingOptions.AllowFragments) as! [Dictionary<String, AnyObject>]
dataFromService is the data that you have received from web service.
Answer for reference pruposes. How to do this in Objective-C
1- First get the info
a) If you are getting the JSON from an API or any online site:
//Set url of the JSON
NString *urlReq = #"http://www.URLHERE.com/PATH/TO/THE/JSON"
//Get data from the JSON
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlReq]];
//Parse JSON
if(jsonData != nil){ //If the response is nil, the next line will crash
NSArray *resultArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
//Do stuff with the result...
}
b) If you are getting the information from the Core Data:
//Get context
NSManagedObjectContext *context = [self managedObjectContext];
//Preapre your fetch
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Products" inManagedObjectContext:context];
NSFetchRequest *requestCoreData = [[NSFetchRequest alloc] init];
[requestCoreData setEntity:entityDescription];
//add predicates if needed
//Execute fetch
NSArray *resultArray = [context executeFetchRequest:requestCoreData error:nil];
//do stuff with the result....
2- Then parse the retrieved info
a) If you want a specific index:
NSString *description = resultArray[0][#"description"];
b) if you don't know what is the index you want (most likely what happens in your JSON):
BOOL found = NO;
int index = 0;
while(index < [resultArray count] && !found){
if([resultArray[index][#"description"] isEqualToString:#"Juice"])
found = YES;
else
++index;
}
if(found){
//'index' => index where the info you are searching can be found
}
else{
//The info couldn't be found in the array
}
There are quite many questions about this kind of question, but I can't find any for CoreData. The problem is: I want to update a large amount of NSManagedObject (you can think of resetting a property's of a large amount of NSManagedObject).
Right now, all I can think of is:
Fetch all object.
Loop through all of it, using forin-loop.
Setting the property in each for block.
The data might be large, and I also want to write a Utility for this action. So the question is:
Is there any more efficient way to perform this? I don't believe using for-loop is efficent.
BONUS QUESTION
Also, I would like to delete all object that satisfied a condition (most likely a boolean flag). My solution is rather simple like the one above:
Fetch all object, with NSPredicate and condition.
Loop through all, forin.
Delete each one of it.
Same question for solution.
The real problem
The real problem is, I want to set all the object's flag (call it willDelete) = YES. Then synchronize from server, then update them and set willDelete = NO. After that, whichever has willDelete = YES would be delete from context.
EDIT 1
My question might be different this one. I want to update the property first. And I care about performance time, not the memory.
EDIT 2
Okay, I managed to use NSBatchUpdateRequest. But the problem is: I got nsmergeConflict. Not sure what to do with this progress. Here's the code anyway:
- (void)resetProductUpdatedStatus
{
NSBatchUpdateRequest *request = [NSBatchUpdateRequest batchUpdateRequestWithEntityName:NSStringFromClass([Product class])];
request.propertiesToUpdate = #{#"hasUpdated" : #(NO)};
request.resultType = NSUpdatedObjectIDsResultType;
NSBatchUpdateResult *result = (NSBatchUpdateResult *)[[CoreDataUtil managedObjectContext] executeRequest:request error:nil];
[result.result enumerateObjectsUsingBlock:^(NSManagedObjectID *objId, NSUInteger idx, BOOL *stop) {
NSManagedObject *obj = [[CoreDataUtil managedObjectContext] objectWithID:objId];
if (!obj.isFault) {
[[CoreDataUtil managedObjectContext] refreshObject:obj mergeChanges:YES];
}
}];
}
This will set all hasUpdated = NO. Next, I'll perform the sync progress. With all the products caught from the synchronization will update the hasUpdated = YES. Next perform delete:
- (void)updateProductActiveStatus
{
NSBatchUpdateRequest *request = [NSBatchUpdateRequest batchUpdateRequestWithEntityName:NSStringFromClass([Product class])];
request.predicate = [NSPredicate predicateWithFormat:#"hasUpdated = NO"];
request.propertiesToUpdate = #{#"isActive" : #(NO)};
request.resultType = NSUpdatedObjectIDsResultType;
[[CoreDataUtil managedObjectContext] executeRequest:request error:nil];
}
As you can see, I've deleted the merge in the update status. So probably, it cause merge conflict in the reset status. So, I guess I will have to fix the merge progress. So I'll ask to people here if you have any idea about this.
Here you can also check the time taken for badge update is quite less than what happens in normal update .As in batchupdate we don't need to fetch the data from file itself. Also check my github link : https://github.com/tapashm42/CoreDataInSwift.git .
I am working on the batchdelete part as well.
func batchUpdate(entityName: String,context:NSManagedObjectContext) {
let batchUpdateRequest = NSBatchUpdateRequest(entityName: entityName)
batchUpdateRequest.propertiesToUpdate = ["name":"Tapash Mollick","designation":"Associate IT Consultant"]
batchUpdateRequest.resultType = .updatedObjectIDsResultType
do {
let start = Date().currentTimeMillis
let batchUpdateResult = try context.execute(batchUpdateRequest) as! NSBatchUpdateResult
let result = batchUpdateResult.result as! [NSManagedObjectID]
print("time taken to update\(Date().currentTimeMillis - start)")
for objectId in result {
let manageObject = context.object(with: objectId)
if (!manageObject.isFault) {
context.stalenessInterval = 0
context.refresh(manageObject, mergeChanges: true)
}
}
}
catch{
fatalError("Unable to batchUpdate")
}
}
extension Date{
var currentTimeMillis: Int64 {
return Int64(Date().timeIntervalSince1970 * 1000)
}
}