I couldn't find this anywhere so I thought I'd ask here.
I'm using Realm in Swift and I'm having trouble to get an object out of the Results at a certain index. I'm using it inside my UITableViewController. I'm making a var at the beginning of the class:
var tasks:Results<Task>?
And then to get it I .objects(type: T.Type):
tasks = realm.objects(Task)
I was hoping to be able to do something like this:
let task = tasks!.objectAtIndex(1)
Is this a limitation or is there another way to do this?
Use standard indexing syntax to retrieve the value:
let task = tasks![1]
Since tasks is an optional, it could be nil. A safer way to write this would be to use optional binding with optional chaining:
if let task = tasks?[1] {
// use task
}
Related
I have a function that returns a completion. The completion has as in parameter a custom class with a dictionary that uses an enum as an identifier.
The problem I am facing is that Xcode does not recognises the type of the variable and does not auto complete when I use it inside of completion block.
My code looks like that
Function
func testFunc (completion:(PrxServiceResponseCallback) ->()){
let responseCallback = PrxServiceResponseCallback()
completion(responseCallback)
}
Class
class PrxServiceResponseCallback:NSObject{
var success = false
var resultCode:Int32 = 0
var response:[PrxResponseAttributes:Any] = [PrxResponseAttributes:Any]()
}
enum PrxResponseAttributes{
case sourceProtocolInfoArray
case sinkProtocolInfoArray
case connectionIDsArray
case connectionInfo
}
Calling the function
testFunc { (testResonse) in
testResonse.response[.]//Not Auto completing
}
The whole idea of making a dictionary with an enum as identifier was to make easier which attributes the dictionary returns but, If I can't auto complete, the idea is more pointless.
Any ideas?
Xcode doesn't give you autocomplete doesn't mean that your code doesn't compile. It just means that Xcode is too stupid to figure things out. I have encountered such situations many times before. It seems to always happen inside closures.
You can just ignore the fact that no autocomplete shows up and type the case name yourself:
testResonse.response[.sourceProtocolInfoArray]
It will compile.
You can also consider creating a struct instead of storing the values in a dictionary. Here's a sample struct (I guessed the types):
struct Response {
let sourceProtocolInfo: [String]
let sinkProtocolInfo: [String]
let connectionIDs: [Int]
let connectionInfo: String
}
If you use var response:[UIColor:Any] = [UIColor:Any]()
it is still not giving auto complete so it is not your issue and you can not do much on it .
If you need auto complete then use PrxResponseAttributes with . (dot)
you can do it like
testResonse.response[PrxResponseAttributes.sourceProtocolInfoArray]
EDIT
Note: It is only happening with implemented closure if you add one property in your PrxServiceResponseCallback class like
var anyValue:Any? {
return response[.connectionInfo] // it is showing completion
}
Im making a Fitness app to learn Core data, and I have found that I need to let the user store every performed workout as a WorkoutLog item, and in there, there should be a series of ExerciseLogs which represents performances of that exercise (it contains each lift and also a reference to the actual exercise design).
Problem is that after a while i realize that i need to have these ordered, so that the next time i want to show the user their workout, the order that the exercisese were performed should be the same.
So I checked "ordered" in the top right of the image there, and now my code is in dire need of an update. I have tried to read as much as I could about working with NSOrderedSet and how to fetch them from core data and then manipulate them, but I havent really found much of use to me. (I have no experice in objective-c)
For example my code that used to be:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
guard let exerciseLogsToDelete = workoutLogToDelete.loggedExercises as? Set<ExerciseLog> else {
print("error unwrapping logged exercises in deleteWorkoutLog")
return
}
I get the error: .../DatabaseFacade.swift:84:77: Cast from 'NSOrderedSet?' to unrelated type 'Set' always fails
So what ive learned about sets and core data no longer seems applicable.
Im far from an expert in programming, but im very eager to learn how to get access to the loggedExercises instances.
TLDR; Is there a way to cast NSOrderedSet to something I can work with? How do we usually work with NSManagedSets from core data? Do we cast them to Arrays or MutableSets? I would very much appreciate an example or two on how to get started with retrieving and using these ordered sets!
Thanks
For anyone else wondering how to get started with orderedSets in core data:
After setting my the WorkoutLog.loggedExercises "to-many" relationship to be ordered, I managed to access them through the mutableOrderedSetValue function like this:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
let orderedExerciseLogs: NSMutableOrderedSet = workoutLogToDelete.mutableOrderedSetValue(forKey: "loggedExercises")
let exerciseLogsToDelete = orderedExerciseLogs.array
for exerciseLog in exerciseLogsToDelete {
guard let exerciseLog = exerciseLog as? ExerciseLog else {
return
}
Works great so far.
And to rearrange the NSOrderedSet I ended up doing something like this:
// Swap the order of the orderedSet
if let orderedExerciseLogs: NSOrderedSet = dataSourceWorkoutLog.loggedExercises {
var exerciseLogsAsArray = orderedExerciseLogs.array as! [ExerciseLog]
let temp = exerciseLogsAsArray[indexA]
exerciseLogsAsArray[indexA] = exerciseLogsAsArray[indexB]
exerciseLogsAsArray[indexB] = temp
let exerciseLogsAsOrderedeSet = NSOrderedSet(array: exerciseLogsAsArray)
dataSourceWorkoutLog.loggedExercises = exerciseLogsAsOrderedeSet
}
If you have a collection type in Swift (like a Set<T>) then you can remove something from the set like...
var s = // some set
s.remove(someElement)
and it will mutate s and remove the someElement from it.
However, this is a mutating function.
Is there a non-mutating function that would return a new set? Like...
let smallerSet = largerSet.removing(someElement)
I could use filter but then it turns this from a O(1) into an O(n) time problem.
If there isn't one already I can write one myself. Is there a convention for the name of a non-mutating function like this?
You can use subtracting function of the Set. For that you need to put that element to be deleted in another Set.
let elementToBeDeleted = Set(arrayLiteral: 1)
var wholeSet = Set(arrayLiteral: 1,2,3)
wholeSet = wholeSet.subtracting(element)
print(wholeSet) //This would print [2,3].
Hope this what you need to get things done.
I have an array of data that doesn't need to be a managed List, meaning I don't need Realm to create a new model for the items with links and the ability to query on the items, etc. I just want a simple array, typically of primitives that don't inherit from Object anyway, that will be persisted with my main object.
The only solution I can think of is to use NSData and NSKeyedArchiver/NSKeyedUnarchiver. Is that the best/only way to do this? Should I just use List even if I don't think I'll need it — what's the best practice for this situation?
Realm doesn't support arrays of primitives (although that functionality is coming soon), so the most straightforward solution is to use a List filled with model objects that just wrap your primitives. There's nothing wrong with archiving to and from NSData and storing the data in your Realm model, though, if you feel that better suits your particular use case.
Here's how I decided to deal with this:
var instructions: [String] {
get {
if _instructions == nil {
_instructions = NSKeyedUnarchiver.unarchiveObject(with: instructionsData) as? [String]
}
return _instructions!
}
set {
instructionsData = NSKeyedArchiver.archivedData(withRootObject: newValue)
_instructions = newValue
}
}
fileprivate var _instructions: [String]? = nil
dynamic var instructionsData = Data()
override static func ignoredProperties() -> [String] {
return ["instructions", "_instructions"]
}
That way I can use the array as I normally would and still have it persisted in a simple way (without having to create an actual List and having to manage a bunch of new models/objects).
https://github.com/mateo951/ISBN-Vista-Jera-rquica- Github Link
The structure I have is supposed to be appending values after an internet search. The internet search is called within a function and returns two strings and an image. When I try to append the returned values in the structure, the image is saved but strings are nil.
var datosLibros = [bookData]()
#IBAction func Search(sender: UITextField) {
let (title1, author1, cover1) = (internetSearch(sender.text!))
let libro = bookData(title: title1, author: author1,image:cover1)
datosLibros.append(libro)
print(datosLibros)
}
The saved structured that is printed to the console is the following:
bookData(title: "", author: "", image: <UIImage: 0x7f851a57fbf0>, {0, 0})
Structure:
struct bookData {
var title: String
var author: String
var image: UIImage
init(title: String, author: String, image: UIImage) {
self.title = title
self.author = author
self.image = image
}
}
Thanks in advanced for any advice of help provided. I'm new to swift so there are a lot of stuff uncovered.
The problem is not with the code you posted but with internetSearch.
But before I explain what is going on there, just a quick note about Swift structs. Structs come with one free initializer that takes as its parameters one value for each stored property defined on the struct. Argument labels correspond to the variable labels.
So for your struct bookData (which really should be BookData since types should be capitalized), you do not need to include that initializer you wrote because it will be automatically provided for you as long as you do not create any additional BookData initializers.
Now for the reason your results are not what you expect. Your Strings are not coming back as nil. Instead, they are coming back as empty Strings, or "". In Swift, "" is very different from nil, which means a complete absence of a value. So your Strings are indeed there, they are just empty.
Okay, our Strings are coming back empty. How about our image? No, our image is not coming back either. You thought it was because you saw a UIImage reference printed in the console, but if you look closer you will notice it is a bogus image. Notice "{0, 0}" after the memory address for the instance. As far as I'm aware, this means the image has a size of 0 x 0. How many useful images do you know that have a size of 0 x 0?
So now we have discovered that our Strings are coming back empty and effectively so is our image. What is going on here?
Well, in your implementation of internetSearch I found on GitHub, this is the first thing you do:
var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()
Naturally, you do this so that you have some variables of the correct types ready to plop in some actual results if you find them. Just for fun, let's see what the result of the code above would be if there were no results.
Well, the initializer for String that accepts no parameters results in an empty String being created.
Okay, how about our image. While the documentation for UIImage does not even mention an initializer that takes no parameters, it does inherit one from NSObject and it turns out that it will just create an empty image object.
So we now have discovered that what internetSearch is returning is actually the same as what it would be if there were no results. Assuming you are searching for something that you know exists, there must be a problem with the search logic, right? Not necessarily. I noticed that your implementation of the rest of internetSearch relies on an NSURLSession that you use like so:
var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) -> Void in
// Lots of code that eventually sets the three variables above to a found result
}
task.resume()
return (bookTitle, bookAuthor, bookCover)
That seems fine and dandy, except for the fact that NSURLSession performs its tasks asynchronously! Yes, in parts you even dispatch back to the main queue to perform some tasks, but the closure as a whole is asynchronous. This means that as soon as you call task.resume(), NSURLSession executes that task on its own thread/queue/network and as soon as that task is set up it returns way before it completes. So task.resume() returns almost immediately, before any of your search code in the task actually runs, and especially before it completes.
The runtime then goes to the next line and returns those three variables, just like you told it to. This, of course, is the problem because your internetSearch function is returning those initial empty variables before task has a chance to run asynchronously and set them to helpful values.
Suggesting a fully-functional solution is probably beyond the scope of this already-long answer, but it will require a big change in your implementation detail and you should search around for using data returned by NSURLSession.
One possible solution, without me posting any code, is to have your internetSearch function not return anything, but on completion of the task call a function that would then append the result to an array and print it out, like you show. Please research this concept.
Also, I recommend changing your implementation of internetSearch further by declaring your initial values not as:
var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()
…but as:
var bookTitle: String?
var bookAuthor: String?
var bookCover: UIImage?
This way, if you find a result than you can represent it wrapped in an Optional and if not you can represent that as nil, which will automatically be the default value of the variables in the code directly above.