Best way to display swiftyjson parsed data? - swift

After already having parsed my data through swiftyjson I am looking to display the parsed information in a uitableview. Let's say I have a list of names that I parsed out of the json file. I would like those names to display each in their own respective cells to allow the user to view them all.
I would also assume that if they selected the selected cells which are being displayed strings now after being parsed that I can actually have those stored into a core data object storing them as returnable strings? The thing is I used the .append function onto an array that I created we'll call names = [String]() to actually allow the values to actually come in order for the cells.
But when doing this I noticed a huge spike in cpu usage doing this as it keeps appending them in order lets say first name is matt second name is john and so forth then it creates a new situation of the array every time. so the first time it reads just matt then the next println would print out matt, john then the next one prints out matt, john, tom and so forth until the list of json names is completed.
If I have 500 names or more it takes the cpu awhile to process all those names into the array. I know there has to be another way but all references I see all use the .append method. So my code would be names.append(Name). Name is a parsed string from swiftyjson. From there I am actually able to populate the tableview which is fine it displays correctly in the end but it takes the device awhile to populate as it first needs to process adding all 500 names each time the parse is called. I am hoping to allow the 500 names be rechecked every time in case new ones have been added. This all stores currently as a local json file already.
The code I am using is below I can figure out the UItableview part but it's more a question of whether appending an array is the best and most efficient way of displaying the parsed data from swiftyjson.
let path = NSBundle.mainBundle().pathForResource("List", ofType: "json")
let jsonData = NSData(contentsOfFile: path!, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: nil)
let json = JSON(data: jsonData!)
for (key, subJson) in json["array"] {
if let Name = subJson["Name"].string {
NameList.append(Name)
println(NameList)
}
}

maybe the Swift map function is more effective
…
let json = JSON(data: jsonData!)
var nameList = [String]()
if let subJson = json["array"] as [Dictionary<String,NSObject>]? {
nameList = subJson.map { $0["name"] as! String }
}

Related

How can I bulk set the value of some results, while limiting how many I update?

In the code below, I like the simplicity of calling
results.setValue, rather than iterating over an array and calling it a bunch of times.
It seems likely that there is some way to do this without iterating over an array, but I guess really my concern is that it appears to be significantly slower to iterate. In my testing, for 500 results, it took just under 3 milliseconds to update them in bulk, vs. 537 milliseconds to iterate.
Seems like there has got to be a built in way to set the value for a subset of the results. Limiting the results by a count doesn't appear to be supported, due to the lazy nature, but I don't see any simple way to update them in bulk. I could order them by a unique field, and get the 500th and then filter I suppose to get a new result set, but seems like there should be a better way to do it.
var results = realm.objects(CloudUpdate.self).filter("status = %#", "queued")
let limitedResults = updates[0..<500]
try! realm.write {
// this works, except it sets all the results to posted
// results.setValue("posted", forKey: "status")
// I'd like to be able to do
// limitedResults.setValue("posted", forKey: "status")
// or something rather than iterate as below
// -- note that limitedResults gets smaller as we set them
// because of the filter on the results.
while limitedResults.count > 0 {
limitedResults[0].setValue("posted", forKey: "status")
}
}
Realm can do that update using key paths of the object, and the performance over large datasets is very good.
The use case is not clear but if you have a dataset and want to update the first X number of objects, this will do it
Given a person class with a name property
class PersonClass: Object {
#Persisted var name = ""
}
and you want to update the first three names to... "Jay"
let peopleResults = realm.objects(PersonClass.self)
let peopleList = RealmSwift.List<PersonClass>()
peopleList.append(objectsIn: peopleResults[0...2]) //see note
try! realm.write {
peopleList.setValue("Jay", forKey: "name")
}
Keep in mind though, as soon as realm objects are manipulated by high level Swift functions, the performance will degrade and more importantly, those objects are no longer lazily loaded - they are all loaded into memory and could potentially overwhelm the device.
One other thing to note is that Realm has no pre-defined ordering so ensure the results have a .sorted(byKeyPath: if you want to update objects by an order.

I want to store formatted text in Realm Swift database

noob swift guy here.
I have this UITextView where the user writes something. I need to store his "note" to the database with all the formated text in it. That means if it has bold, I need to display it as bold later.
This text should be stored in local Realm Swift Database. Any solutions how can I achieve that? I head about "Markdown", but I didn't understand how can that be useful.
Much thanks! Have a great day!
Realm only stores ascii text and cannot directly store any formatting properties like BOLD, and that's true for most databases.
If you want to store formatted strings, one option is to archive it to a Data type, which Realm fully supports. Then, when reading data back in, unarchive it from Data back to a formatted string. (formatted strings are often called Rich Text or in code: attributedStrings)
The below code is macOS but iOS will be similar
A quick example - here's a class to hold a some attributed string data
class MyAttrStringClass: Object {
#Persisted var attrStringData: Data!
}
Suppose we have a (rich) text field with the following text
Hello, World
To store the class with the attributed string in Realm:
let s = self.myRichTextField.attributedStringValue
let data = try! NSKeyedArchiver.archivedData(withRootObject: s, requiringSecureCoding: false)
let myObject = MyAttrStringClass()
myObject.attrStringData = data
try! realm.write {
realm.add(myObject)
}
then when reading back from Realm and displaying the attributed string in that same field
let myObject = realm.objects(MyAttrStringClass.self).first!
let data = myObject.attrStringData!
let s = try! NSKeyedUnarchiver.unarchivedObject(ofClass: NSAttributedString.self, from: data)
self.myRichTextField.attributedStringValue = s!
Note: there's no error checking in the above. Please protect your code by safely unwrapping optionals.

How to find array value of a data model in a view container of specific row of that array that user typed?

I don't know how to find array value of that particular index which user has typed. I had connected that array to my data model which contain 2 values ?
I have tried .contain value after implementing a new variable of the data model but it hasn't worked
let productsNamess = document.data()["ProductName"] as! String
let quantityGame = document.data()["Quantity"] as! String
// making a new net stock data incoming class
let newClassForProducts = dataModelOutgoing()
newClassForProducts.productName = productsNamess
newClassForProducts.quantity = Int(quantityGame)
// forward data to main dataincoming main class
self.dataIncoming.append(newClassForProducts)
I dont know how to take data out of an array which contains two values in one array.

Retrieving NSOrderedSet from Core Data and casting it to entity managedObjectSubclasss

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
}

Function returning specified values but structure doesn't append its values

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.