iOS 9 Realmswift trouble with query - swift2

I am trying to get a Results<News> of news objects from realm of all objects that have 'mytag'
The News object looks something like
dynamic var id = 0
dynamic var title = ""
dynamic var date = NSDate()
dynamic var modified = NSDate()
dynamic var protected = true
dynamic var category : Category?
dynamic var image : Image?
let content = List<Content>()
let tags = List<Tag>()
I have a Results<Tag> with all my tags. Tag has a boolean my to see if it belongs to my tags.
This way I could get personal news.
However, I don't understand how to query this. I have some knowledge of SQL, but i cant seem to figure it out using contains or in
I tried a workaround but it seems Results does not have an append function.
Here's my current workaround:
func retrieveMyNewsSortedByDate() -> Results<News> {
let myTags = TagDataService().myTagsList() // retunrs a List<Tag>
print("My news items");
let items = database().objects(News).filter("tags IN %#", myTags).sorted("date") // how to query or query with news and tag table
let myTagItems = List<News>()
for tag in myTags {
for news in items{
for newsTag in news.tags {
if newsTag == tag {
myTagItems.append(news) // Results does not have .append or .addobject
}
}
}
}
mytagItems = Results(myTagItems)
return myTagItems
}
However, now I would have a very inefficient way that also outputs list that I can't seem to cast to Results. How do I do this?

well, it was quite easy in the end :D
func retrieveMyNewsSortedByDate() -> Results<News> {
let myTags = TagDataService().myTagsList()
let items = database().objects(News).filter("ANY tags IN %#", myTags).sorted("date")
return items
}

Related

Refresh values in a view that displays the sum of an attribute from CoreData in SwiftUI

I am working on a personal app to help me log income and calculate tax etc
I have a view that displays the totals and does a little math to calculate tax and profits etc. To get the sum values I use the following function:
func sumRevenue() -> Double {
var revenueTotal : Double = 0.00
let expression = NSExpressionDescription()
expression.expression = NSExpression(forFunction: "sum:", arguments:[NSExpression(forKeyPath: "revenue")])
expression.name = "revenueTotal";
expression.expressionResultType = NSAttributeType.doubleAttributeType
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Log")
fetchRequest.propertiesToFetch = [expression]
fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
do {
let results = try managedObjectContext.fetch(fetchRequest)
let resultMap = results[0] as! [String:Double]
revenueTotal = resultMap["revenueTotal"]!
} catch let error as NSError {
NSLog("Error when summing amounts: \(error.localizedDescription)")
}
return revenueTotal
}
In my CoreData model, "Log" is the entity and in this case, "revenue" would be the attribute. All works well and the totals display in the TotalsView by using "sumRevenue", however the totals don't update in that View when I add a new log, but they are added to the list of logs just fine and do update if I rebuild.
I know the answer probably has something to do with ObservedObject but I am new to all this and I am struggling with it. Any advice would be much appreciated.

Xcode Coredata: Fetch two values from one CoreData entity and insert to another CoreData entity within loop function

Im very new with CoreData fetching/display and so far able to save into CoreData from a JSON fetch.
The fetched data is an array of Airport info with only three items; airport_code, access_point and image_url.
I need to add two more values to each fetched item - a lat and lon coordinate which is stored in another CoreData entity with a matching airport_code item/attribute.
Can anyone provide some guidance as to how to create a separate function to query this other CoreData during the loop sequence by using the predicate value of the airport_code? I have attached the code I have so far:
func saveData(context: NSManagedObjectContext){
xArray.forEach { (data) in
let entity = Airports(context: context)
entity.airport_code = data.airport_code
entity.access_points = data.access_points
entity.image_url = data.image_url
entity.lat = getLat()
entity.lon = getLon()
}
do{
try context.save()
print("Success Saving to CoreData: \(xArray.count)")
}
catch{
print("Error Saving to CoreData \(error.localizedDescription)")
}
}
func getLat() -> String {
#FetchRequest(entity: AllAirports.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \AllAirports.airport_code, ascending: true)])
var results: FetchedResults<AllAirports>
//this is where Im lost as to how to query this CoreData to fetch the LON value when there is a match to the data.airport_code in the loop above.
return latResults
}
func getLon() -> String {
#FetchRequest(entity: AllAirports.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \AllAirports.airport_code, ascending: true)])
var results: FetchedResults<AllAirports>
//this is where Im lost as to how to query this CoreData to fetch the LON value when there is a match to the data.airport_code in the loop above.
return lonResults
}
func fetchData(context: NSManagedObjectContext) {
// this function performs a JSON parse and returns the xArray above
….
First you want to take a look at the response here What is the best way to do a fetch request in CoreData? to get an idea how a programmatic fetch request may look like.
As I understand your problem now, you want to write two methods getLat() and getLon() which are going to fetch the coordinates for an airport.
I would recommend to write a single function as shown below which looks up your airport in AllAirports using the given code and returns the tuple with the coordinates found.
func getCoord(airport_code: String) -> (lon: Double, lat: Double)? {
let request: NSFetchRequest<AllAirports> = AllAirports.fetchRequest()
request.predicate = NSPredicate(format: "airport_code == %#", airport_code)
if let result = try? viewContext.fetch(request) {
print("Found \(result.count) airports matching \(airport_code)")
// Just return the first matched airport
if let first = result.first {
return (first.longitude, first.latitude)
}
}
return nil
}
A better solution would be to add an extension to you AllAirports which returns an entry for a given (hopefully unique!) airport_code:
extension AllAirports {
static func airport(byCode airportCode: String, in context: NSManagedObjectContext) -> AllAirports? {
let request: NSFetchRequest<AllAirports> = fetchRequest()
request.predicate = NSPredicate(format: "airport_code == %#", airportCode)
if let result = try? context.fetch(request) {
return result.first
}
return nil
}
}
This could then be used in your code as follows:
func saveData(context: NSManagedObjectContext){
xArray.forEach { (data) in
let entity = Airports(context: context)
entity.airport_code = data.airport_code
entity.access_points = data.access_points
entity.image_url = data.image_url
if let airport = AllAirports.airport(byCode: data.airport_code, in: context) {
entity.lat = airport.latitude
entity.lon = airport.longitude
}
}

Realm search Database with Predicate in Swift 2

I write an (Mac)App in Swift2 which should search a Realm database of Teachers with a sepcific subject. The Definition for the Teacher Object in realm looks like this:
class Teacher: Object {
var subjects = List<Subject>()
}
This class is very complex so I deleted some lines...
Here's the function that should filter the Database for Teachers with Specific subjects and give only the Teacher names back (as String Array: [String]):
func getAllTeacherNamesForSubject(subject: String) -> [String] {
// Open Database
let realm = openRealmDatabase()
// Or open it this way:
// RLMRealm.setDefaultRealmPath("/Users/name/Data.realm")
// var realm: Realm!
// realm = try! Realm()
// filter the Database with Predicate
let objects = realm.objects(Teacher).filter("!!! Need Help !!!", subject)
// How can I filter the Database? I want to get all the Teachers for that subject
// Make an Array of Objects
let objectsArray = objects.toArray(Teacher) as [Teacher]
// Return
return ???
}
// This is the toArray extension.
// You may need it to use the snippet above
extension Results {
func toArray<T>(ofType: T.Type) -> [T] {
var array = [T]()
for var i = 0; i < count; i++ {
if let result = self[i] as? T {
array.append(result)
}
}
return array
}
}
So the problem is that I don't know how to filter the Database.
Can someone help me please?
To filter objects that have relations with specific values, or in your case, teachers that have subjects with a specific name, you could use this predicate for a case-insensitive search:
NSPredicate(format: "ANY subjects.name ==[c] %#", subjectName)
and just plug that into the filter function. Since you want to return only the teacher names, you won't need to create any extensions as suggested, but rather use the native swift map method:
func getAllTeacherNamesForSubject(subjectName: String) -> [String] {
let realm = openRealmDatabase
let predicate = NSPredicate(format: "ANY subjects.name ==[c] %#", subjectName)
return realm.objects(Teacher).filter(predicate).map { $0.name }
}
For reference, you can find a great cheatsheet on Realm's website describing the complete list of supported predicate syntax.

Converting JSON from AlamoFire/SwiftyJSON to Dictionary in Swift/Xcode

My head is going to explode :) - I've been trying to get a JSON String from my server to a Dictionary Value, and I can't get it to work.
I'm trying to get (from my Server - this is dynamic and I want my app to be able to pull new data from the server when needed):
{"1":"Location 1","2":"Location 2","3":"Location 3"}
To this Dictionary in Xcode using Swift:
var labels = [
1 : "Location 1",
2 : "Location 2",
3 : "Location 3"
]
This has got to be pretty straight forward, but for the life of me I can't figure it out...
Here's my Swift - I can get it to pull the information from the server, but I can't get it into a dictionary like I need
var postEndpoint: String = "http://www.myserver.net/app/campus.php"
Alamofire.request(.GET, postEndpoint)
.responseJSON { (request, response, data, error) in
if let anError = error
{
println("error")
println(error)
}
else if let data: AnyObject = data
{
let post = JSON(data)
println(post)
}
}
which results in:
{
"1" : "Location 1",
"2" : "Location 2",
"3" : "Location 3"
}
The End Result that I'm using this for is an iBeacon implementation with the following code:
let knownBeacons = beacons.filter{ $0.proximity != CLProximity.Unknown }
if (knownBeacons.count > 0) {
let closestBeacon = knownBeacons[0] as CLBeacon
let locationID = post[closestBeacon.minor.integerValue]
self.locationLabel.text = locationID
self.view.backgroundColor = self.colors[closestBeacon.minor.integerValue]
}
The error I'm getting is at self.locationLabel.text = locationID 'JSON' is not convertible to 'String', I do not get this error when I use the static var labels dictionary. Am I trying to get the data from the server incorrectly? What am I doing wrong??? I think the var labels having an undeclared Type allows Swift to figure out what it needs to, how do I do the same from the JSON part?
Oh you were so close!
Your problem is that your post dictionary is a [String: String] and not an [Int: String] like you think it is. You have a few ways to fix it, but the easiest for now would be to just do the following:
let locationID = post[String(closestBeacon.minor.integerValue)]!
While this will certainly work, a better solution would be to convert your post into a [Int: String] typed dictionary like you expect in the responseJSON closure. Here's how this could work.
let json = JSON(data)
var post = [Int: String]()
for (key, object) in json {
post[key.toInt()!] = object.stringValue
}
You would want to add some safety around what to do if the key or object were not able to be converted to an Int or String respectively, but I'll leave that to you.
If having a [String: String] is sufficient for anyone, he/she could try the following code:
let responseString = "{\"1\":\"Location 1\",\"2\":\"Location 2\",\"3\":\"Location 3\"}"
if let dataFromString = responseString.data(using: String.Encoding.utf8, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
var labels = json.dictionaryObject! as! [String: String]
print(labels)
}
The result is:
["2": "Location 2", "1": "Location 1", "3": "Location 3"].
I solved this by working in reverse. I changed the call. Instead of getting the Dictionary of Values from the Server, I just query the Server with the Single Variable that I already had from the variable
closestBeacon.minor.integerValue
And then get the string that I needed from the server and that solved my problem. Still has the same number of calls to the server, so no additional overhead was added. Sometimes you just have to think outside the box that you put yourself into.
If anybody can solve this the other direction, I'm still eager to hear how it could work.
You need to check recursively for inner JSON as well
Use below function to convert into Dictionary or Array
static func toDictionary(from: JSON) -> Any? {
if from.type == .array {
var array = [Any]()
for _i in 0..<from.count {
array.append(toDictionary(from: from[_i]) as Any)
}
return array
} else if from.type == .dictionary {
var dictionary = [String: Any]()
for (key, value) in from {
dictionary[key] = toDictionary(from: value)
}
return dictionary
} else {
return from.stringValue
}
}

Retrieve ALAsset or PHAsset from file URL

Selecting images in Photos.app to pass to an action extension seems to yield paths to images on disk (e.g.: file:///var/mobile/Media/DCIM/109APPLE/IMG_9417.JPG). Is there a way to get the corresponding ALAsset or PHAsset?
The URL looks like it corresponds to the PHImageFileURLKey entry you get from calling PHImageManager.requestImageDataForAsset. I'd hate to have to iterate through all PHAssets to find it.
I did what I didn't want to do and threw this dumb search approach together. It works, although it's horrible, slow and gives me memory issues when the photo library is large.
As a noob to both Cocoa and Swift I'd appreciate refinement tips. Thanks!
func PHAssetForFileURL(url: NSURL) -> PHAsset? {
var imageRequestOptions = PHImageRequestOptions()
imageRequestOptions.version = .Current
imageRequestOptions.deliveryMode = .FastFormat
imageRequestOptions.resizeMode = .Fast
imageRequestOptions.synchronous = true
let fetchResult = PHAsset.fetchAssetsWithOptions(nil)
for var index = 0; index < fetchResult.count; index++ {
if let asset = fetchResult[index] as? PHAsset {
var found = false
PHImageManager.defaultManager().requestImageDataForAsset(asset,
options: imageRequestOptions) { (_, _, _, info) in
if let urlkey = info["PHImageFileURLKey"] as? NSURL {
if urlkey.absoluteString! == url.absoluteString! {
found = true
}
}
}
if (found) {
return asset
}
}
}
return nil
}
So this is commentary on ("refinement tips") to your auto-answer. SO comments don't cut it for code samples, so here we go.
You can replace your for-index loop with a simpler for-each loop. E.g. something like:
for asset in PHAsset.fetchAssetsWithOptions(nil)
As of the last time I checked, the key in info["PHImageFileURLKey"] is undocumented. Be apprised. I don't think it will get you rejected, but the behavior could change at any time.

Resources