Swift: String unable to read data - arrays

After separating fields with
var fields = row.characters.split {$0 == "\t"}.map { String($0) }
the debugger shows "unable to read data" for one field.
What does it mean, except there is no data? I want to check it, but it is no empty string ("") and it's not nil.

Are you using Swift 2 in XCode 7? Might wanna give this a try:
var fields = row.characters.split {"\t"}.map(String.init)
Or maybe your problem is that row ends with a "\t" and the final string is not valid for String init. I would suggest split and then print your results as debug.
source: Ethan's answer

Related

Swift - JSON Decode into Struct Contains Against String

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

Ambiguous use of 'init' in Array mapped to String with chosen separators

As #vadian suggested, I am learning Xcode from Paul Hudson's 100 days of SwiftUI to better understand the basics (even though I am still struggling with time to deliver working GitHub repository search - no it is not for the job I am working on right now).
I am doing a lesson on Arrays, so pretty basic stuff right now and I struggle to map the String from array on the sorted array. I don't get why, but I get the:
Ambiguous use of 'init'
error.
Here's the code:
let cities = ["London", "Tokyo", "Rome", "Budapest"]
print(cities.sorted())
let citiesSorted = cities.sorted()
let citiesSortedString = citiesSorted.map(String.init).joined(separator:", ")
And it's so really strange, since I pulled the similar thing out before in the same Playground but not with the sorted Array:
var schoolScoresString = schoolScores.map(String.init).joined(separator:", ")
print(schoolScoresString)
And yes I tried changing let to var. It didn't help.
cities is already of type [String], so you're passing a String to String.init, which obviously won't work. If you want to join an array of Strings into a single String, remove the map and just call joined on the array.
let citiesSortedString = citiesSorted.joined(separator:", ")

Error when trying to set array in userdefaults: Thread 1: "Attempt to insert non-property list object

I have solved the issue now, thanks for your help. I shouldn't have tried to save arrays with UITextViews, but I should have saved their text as strings instead. Here was the original question:
I have tried a lot, and googled a lot, but I can't solve this problem on my own. Whenever I try to save an array in userdefaults, it just is not working. I get the following error:
Thread 1: "Attempt to insert non-property list object (\n "<UITextView: 0x14001f800; frame = (0 0; 355 180); text = 'D'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600003f01d10>; layer = <CALayer: 0x6000031c83e0>; contentOffset: {0, 0}; contentSize: {355, 30}; adjustedContentInset: {0, 0, 0, 0}>"\n) for key content"
I don't know what a non-property list object is. And I do not know how to solve the problem. Below is the lines of code that do not work.
var contentList: [Any] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
contentList.append(label)
defaults.setValue(contentList, forKey: "content")
If I take out the last line of code by turning it into a comment everything runs just fine. How should I replace that line of code? I essentially want to save an array of UITextViews and make it larger every time I call a fucntion (this code is part of a larger function). The reason why I have created another two lists (cl and contentList) is that it helps me with a problem down the line. What I cannot understand however, is why the last line of code doesn't work. If anyone has any ideas, please help me, it would be much appreciated.
Use only String as stated in comments :
var contentList: [String] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
If lbText = label.text {
contentList.append(lbText)
defaults.setValue(contentList, forKey: "content")
}
You can only store a very limited list of data types into UserDefaults, commonly referred to as "property list objects" (Since property list (or plist) files will only store the same data types.
To quote the Xcode docs on UserDefaults, in the section titled "Storing Default Objects":
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary [or Data, String, NSNumber, Date, Array, or Dictionary types in Swift.] If you want to store any other type of object, you should typically archive it to create an instance of Data.
(I added the equivalent Swift types to the above quote in square brackets, since it looks like Apple hasn't updated it for Swift.)
That's worded a little awkwardly. The idea is that you can only store data of the types listed. Because the Array and Dictionary types are "container" types, you can store any combination of arrays and dictionaries that contain combinations of any of the above types. For example, you can store an array that contains a dictionary, 3 dates, 2 floats, a Double, some Data, and 2 arrays, and those dictionaries and arrays can contain other dictionaries and/or arrays.)
It is almost always wrong to archive UIView objects like UITextViews. You should save the text properties of your text views instead.
If you want to manage a vertical stack of UITextView objects, I suggest adding a vertical stack view to your user interface, and then writing code that adds or removes UITextView subviews to your stack view. You should be able to find plenty of examples of adding and removing objects from stack views online. (It's really easy.)
If you want to manage a scrolling list of feeds of arbitrary length, you might want to use a table view or collection view instead. Those require that you set up a data model and implement a "data source". That takes a little practice to get right, but is very powerful.

Swift: How do you use a variable in an array?

I am using a pod called iOSDropDown to display a dropdown selection menu for a textfield. I am getting a list of data from php to populate that selection menu that Im storing in a variable.
The PHP data passed stored in a swift variable looks like this when printed -> "option01","option02","option03"... and so on. This is dynamic data that will change that is why I am retrieving from PHP/MYSQL Database instead of just manually typing in the options in the Swift array.
Below is my code. What I am trying to do is use the "dropdownData" variable that holds the options for the array. Each option should be in its own row and separately selectable. What I am getting is one option, one string of coding with all my options as shown in the picture below.How would I use the dropdownData variable to display options instead of one string, one option?
dropdownData = "option01","option02","option03"... etc. ALL OPTIONS STORED IN THIS ONE ARRAY
let dropdownData : String = (dumpsArray[indexPath.row] as AnyObject).value(forKey: "dropdownData") as! String
.
cell.nameField.optionArray = [dropdownData]
Image
In the image above there should be no comma after styrofoam cooler... the next product should be the next option displaying under the styrofoam cooler and separately selectable.
Seems like dumpsArray[indexPath.row] as AnyObject).value(forKey: "dropdownData") returns a String where names are comma separated,
so
if let nameString = dumpsArray[indexPath.row] as AnyObject).value(forKey: "dropdownData") as? String {
let namesArray = nameString.components(separatedBy: ",")
cell.nameField.optionArray = namesArray
}
That should do

Parse.com Query with swift 1.2 and string array

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)
}
}
})

Resources