I am fairly new to SwiftUI but am pulling my hair out trying to display text from two ForEach loops. I am working on a song app that would display lyrics in stanzas. My data is one array Lyrics that holds Verses which is another array. Each verse is a stanza. And each verse has a string array that stores the lyrics for one line.
//Lyrics variable
#Published var lyrics: [Verse]? = [Verse]()
//Verse structure storing each line
struct Verse: Codable, Identifiable {
let id = UUID()
let verseContent: [String]
}
The part I am having trouble is in implementation of getting all of the information into Text within my View. One ForEach loop works and I can get the first line of each of my stanzas as follows
//Builds Fine with this code
ForEach(lyrics) { verse in
Text(verse.verseContent[0])
}
But the problem is when I try and do a nested ForEach to get all of the lines in each stanza with the following.
return AnyView(
ForEach(lyrics) { verse in
ForEach(verse.verseContent { line in
Text(line)
)
}
)
When I try this I get the following error
Explicitly specify the generic arguments to fix this issue
Generic parameter 'ID' could not be inferred
Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'String' conform to 'Identifiable'
Solved! It just needed .self for both of the ForEach statements
Related
I have a JSON file I am grabbing from a remote server and decoding it. It is being decoded into a struct and then used in an #Published var.
For example:
JSON:
[{"keyword": "foo"}, {"keyword": "blah"}]
Struct:
struct keywords: Codable {
var keyword: String
}
Observable Class:
#Published var keys: [keywords] = []
I need to do a real-time comparison, using contains, against a value the user is entering, utilizing an if statement. I can get it to use the first entry in the keys var and check against any characters that might be in that string, but I cannot get it to work across the entire array on only the full strings (not individual characters).
Here's what correctly checks against only the first entry of the array and each individual character.
if keys[0].keyword.contains(where: blah.contains)
I have also tried mapping it to strings like this (does exactly the same thing):
if (keys[0].keyword.map{String($0)}).contains(where: blah.contains)
Been at this all day but cannot find any docs on how to do this correctly.
The goal is to not just have it use the first entry of the array but the entirety of the entries of the array. I understand that is the [0] but it wouldn't compile without it. I need to make it compare on the entire string, not on individual characters.
For example, if the word blah is the array and the user enters bla it should not match anything. Only if the entire word blah is contained in the user's entry should it match (i.e. fooblahfoo would match because blah is within the string, but foobalbabh would NOT match even though all of the characters contained within the word blah are in that string).
Appreciate any assistance with actual code examples. This is Swift 5.5.
UPDATE:
Not sure where the confusion is coming from but here's an even clear explanation.
There is a TextField called username where the user enters a string.
If the user enters the string fooblahfoo and the word blah is somewhere in the array it should match.
This is easy when you have a simple array of strings like this:
keywords = ["foo", "blah"]
For that you just do keywords.contains(where: username.contains)
However, the decoded JSON is more of a dictionary than a simple array of strings, so you have to call to the key of keyword and look at all the values in order to make the comparison.
First, use contains(where:) on the keys array -- then, check each item to see if the user input contains that keyword.
struct Keywords: Codable {
var keyword: String
}
var keys: [Keywords] = [.init(keyword: "foo"),.init(keyword: "blah")]
var userInput = "fooblahfoo"
var result = keys.contains { userInput.contains($0.keyword) }
print(result)
true
I am currently in a bit of a bind.
struct sectionWithDatesAsName {
var sectionName : String
var sectionObjects : [SoloTransactionModel]!
init(uniqueSectionName: String?, sectionObject: [SoloTransactionModel]?) {
sectionName = uniqueSectionName ?? "nil"
if let section = sectionObject {
sectionObjects = section.reversed()
}
}
}
I currently have an array of sectionWithDatesAsName. And I can work with it, display in the tableView among other things.
The bind comes up when I want to check some information in the sectionObject before displaying it on the tableView.
I want to check the type of the sectionObject which is saved in the object itself.
How do I check the information in the sectionObject without slowing down the app? Or have a horrible time complexity calculated?
(Note: I can't change the format of the struct has this has already been used by a whole lot of other processes)
Write a function in your sectionWithDatesAsName with the signature filteredSections(type: sectionType) -> sectionWithDatesAsName
(If you don't have the ability to edit the definition of sectionWithDatesAsName you can create an extension that adds the above function)
If the sectionWithDatesAsName is defined elsewhere, define this function in an extension.
When you call it, build a new sectionWithDatesAsName object by filtering the arrays to match the specified type.
Use the resulting filtered sectionWithDatesAsName object as the data model for your table view. It will be built once and used for the lifetime of the tableView, so you will pay an O(n) time cost to filter it once when you create it.
I'm working on some code where I have a Food object and I have an array of Food objects: var list: [Food] = [] (this line of code is called right at the beginning as a global variable) I have lines of code that make an array in parse but i'm not really sure it is working, it still says "undefined" in parse when it should be a Food Object array. My question is really simple.
1 - How do I make a Food object array in Parse.com?
2 - How do I get the information from that array and make the parse array a local variable?
3 - How to append that array, I know I can just call .append(customFoodObject) to the local array, is that how I should do it?
4 - How, once i've edited the array, do I save it back to parse?
: Just those 4 questions, i've been told many answers like "just use a query" or "make new objects" but I gave the names of the actual objects because I would appreciate it if you gave code that I could see works and then understand how it works; thanks in advance for all you help.
Anyway the questions demands for a huge manuscript, nonetheless i will try to answer your question with some explanations and code snippets where possible.
Objects contain similar stuffs so they would be a row in a table in a database. After all Parse is a sugar topped database API. So a object would be mapped to a Class in Parse or specifically a row of the class of type Food.
Creating a Food object in Parse is pretty straightforward as the documentation is fairly explanatory.
let food = PFObject(className: "Food")
food["name"] = "Sushi Pizza"
food["price"] = "1100¥"
//saves on the server even if the networks fails now
//caches the result offline and when the phone enters in network it uploads
food.saveEventually(nil)
For storing array of foods do this:
let foodClassName = "Food"
for index in foods!{
let object = PFObject(className: foodClassName)
object["name"] = index.name
object["price"] = index.price
object.saveEventually(nil)
}
Basically you create a Food table with the className and then insert similar objects and save it.
Getting array is querying the parse database. All you have to know is the name of the Class Parse uses. In our case we had if "Food" stored in a constant.
let query = PFQuery(className: foodClassName)
//query.fromLocalDatastore() //uncomment to query from the offline store
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil && objects != nil{
for index in objects!{
(index as! PFObject).pin() //pining saves the object in offline parse db
//the offline parse db is created automatically
self.makeLocalVariable(index as! PFObject) //this makes the local food object
}
}
}
So to save it to locl Food object we initially had we transform the PFObject like so
var localFoods:[Food]? //global scoped variable
func makeLocalVariable(index:PFObject){
let foodname = index.objectForKey("name") as! String
let price = index.objectForKey("price") as! String //we had the currrecny saved too
let foodObj = Food(name: foodname, price: price)
localFoods?.append(foodObj)
}
Yes i did it that way. Thats correct.
4.So the process is basically similar to fetching data. Now what you do is suppose fetch data for Food with name Pizza because we want to increase the price. Heres how you do that.
let queryEdit = PFQuery(className: foodClassName)
queryEdit.whereKey("name", equalTo: "Pizza")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil && objects != nil{
if objects!.count == 0{
//edit
let obj = (objects as! [PFObject]).first!
obj.setValue("2340¥", forKey: "price")
//save it back
obj.saveEventually(nil)
}
}
}
I hoped i answered your questions. Remember a Object can be mapped to Class in Parse or Table or Entity in a Relation Database. Then that table can have many similar instances and you can say they are array of one type.
Let me know if i can help you more. Cheers!
Food for this example is just a Struct but can be easily a class if it has functionalities.
struct Food{
var name:String?
var price:String?
}
I am trying to query from parse.com and I would db receiving about 100 objects per time. I used the swift example code on their website, and the app doesn't build with that code. So I looked around and found that people were using code similar to this:
var query = PFQuery(className:"posts")
query.whereKey("post", equalTo: "true")
query.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
// do something
self.myDataArray = objects as! [String]
})
This does not work, because I am trying to convert PFObject to String
I would need to get the one one value from each object into a swift string array [String]. How do I get just the one text value, instead of the PFObject and how do I get it into the swift string array?
I don't speak swift very well, but the problem with the code is it's trying to cast the returned PFObject to a string, but you want to extract a string attribute, so (if you really want to do it):
for object in objects {
var someString = object.valueForKey("someAttributeName") as String
self.myDataArray.addObject(someString)
}
But please make sure you need to do this. I've noticed a lot of new parse/swift users (especially those who are populating tables) have the urge to discard the returned PFObjects in favor of just one of their attributes. Consider keeping the PFObjects and extracting the attributes later as you need them. You might find you'll need other attributes, too.
For starters, I would definitely recommend using the "if let" pattern to qualify your incoming data. This is a nice Swift feature that will help avoid run-time errors.
var query = PFQuery(className:"posts")
query.whereKey("post", equalTo: "true")
query.findObjectsInBackgroundWithBlock(
{ (objects: [AnyObject]?, error: NSError?) -> Void in
// check your incoming data and try to cast to array of "posts" objects.
if let foundPosts = objects as? [posts]
{
// iterate over posts and try to extract the attribute you're after
for post in foundPosts
{
// this won't crash if the value is nil
if let foundString = post.objectForKey("keyForStringYouWant") as? String
{
// found a good data value and was able to cast to string, add it to your array!
self.myDataArray.addObject(foundString)
}
}
})
This question already has answers here:
Difference between JSONObject and JSONArray
(9 answers)
Closed 6 years ago.
What are the difference and advantages of using JSON arrays:
{
thing:[
{ },
{ }
]
}
versus JSON objects:
{
thing:{
{ },
{ }
}
}
The difference between an array and an object is that
Objects are set up using a key and value like:
person.age = 15;
If the key value is a variable, then one could access it like:
var key = "age";
alert(person[key]);
Arrays use an integer[1] index and take a value.
player[1].score += 1000;
[1] Yes, I know, in JavaScript the integer index is really turned into a string behind the scenes. Ignore that. Think of arrays taking an integer value ESPECIALLY when you think of JSON.
Objects- key and value, Arrays- integer. When do you use this or that?
I think of arrays and objects as "is a/an" and "has a" respectively.
Lets use "Fruit" as example.
Every item in fruit array is a type of fruit.
array fruit : [orange, mango, banana]
.
Arrays can contain objects,strings, numbers, arrays, but lets deal with only objects and arrays.
array fruit : [orange:[], mango:{}, banana:{}]
.
You can see that orange is an array too. It implies any item that goes int orange is a type of orange, say: bitter_orange, mandarin, sweet_orange.
for fruit object, any item in it is an attribute of fruit. thus the fruit has a
object fruit :{seed:{}, endocarp:{},flesh:{}}
This also implies that anything within the seed object should be property of seed, say: colour,
JSON arrays represent a collection of objects. In JS, theres a bunch of collection functions off of them such as slice, pop, push. Objects have just more raw data.
The second form you show is actually not valid JSON, as each of the objects in the "thing" object would need some sort or property name to access it by.
To answer your question, the difference is that in the first case, you would access the objects in "thing" using array access like obj.thing[0] or obj.thing[1]. In the second case, if you had proper property declarations you would access like obj.thing.property
Generally in JSON array are used to store a grouping of like items, while object are used to contain grouping of different properties for a single item.
JSON is primarily a language that allows serializing javascript objects into strings. So upon deserializing a JSON string you should get a javascript object structure. If your json deserializes into an object that stores 100 objects called object1 to object100 then that's going to be very inconvenient.
Most deserializers will expect you to have known objects and arrays of known objects so that they can convert the strings into the actual object structure in the language you're using.
Also this is a question that the philosophy of object oriented design would answer you.
A JSON object can be transformed using toJSON:
function kryptonite(key)
{
var replacement = {};
for(var __ in this)
{
if(__ in alias)
replacement[__] = this[__]
}
return replacement;
}
var alias = {"Clark":"","phone":""};
var contact = {
"Clark":"Kent",
"Kal El":"Superman",
"phone":"555-7777"
}
contact.toJSON = kryptonite;
var foo = JSON.stringify(contact)
A JSON array can be transformed using map:
var contact = {
"Clark":"Kent",
"Kal El":"Superman",
"phone":"555-7777",
"home":[{"present":"Metropolis"},{"past":"Krypton"},{"future":"Phantom Zone"}]
}
var filter = {"past":"","future":""}
function junction(value, index)
{
for (var __ in filter) if(value[__]) return value[__]
}
var island = contact.home.map(junction);