Error Ambiguous reference to member 'indices' in SwiftUI - ios

I am trying to iterate over all the dates I have received from my API and convert them into a usable format for my app. I am running into an error with this code
ForEach(dateList.indices, id: \.self) { date in
self.que = "";
for letter in dateList[date] {
if letter == "T" {
dateList[date] = self.que
return
}
else if letter == "-" {
self.que = self.que + "/"
}
else {
self.que = self.que + letter;
}
}
}
I am trying to have this iterate over each string I have in the dateList array and convert it into a format that is usable in my app. This format is going from 2020-02-28T03:32:44Z to 2020/02/28. I am getting the error "Ambiguous reference to member 'indices'" and I'm not sure what this means.

struct ForEach is only using for showing views:
/// A structure that computes views on demand from an underlying collection of
/// of identified data.
#available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable { ... }
for example you can use it to show rows of some List
List {
ForEach(dateList.indices, id: \.self) { dateIndex in Text("\(self.dateList[dateIndex])") }
}
it's not about computing some variable like que, you should extract this computing into some function and call it from onAppear for example. Here is a little example of ForEach and forEach difference:
struct SomeLoopData: View {
#State var dates = ["2020/02/28", "2020/02/29"]
var body: some View {
List {
ForEach(dates.indices) { index in Text("\(self.dates[index])") }
.onAppear {
self.compute()
}
}
}
func compute() {
dates.indices.forEach { index in
print(dates[index])
}
}
}

Related

How to get an enum from a String?

Minimal reproducible code:
abstract class FooEnum extends Enum {
// Some abstract methods...
}
enum One implements FooEnum { a, b }
enum Two implements FooEnum { x, y }
FooEnum getFooEnum(String string) {
// Too much boiler plate code, how to do it in a better way?
if (string == 'One.a') return One.a;
else if (...) // other cases.
}
Right now I'm doing it manually (error prone). So, how can I get an enum from a String?
If you only want to use pure dart without flutter or any packages you could do this:
FooEnum? getFooEnum(String string) {
final classValue = string.split('.');
if (classValue.length != 2) {
return null;
}
try {
switch (classValue[0]) {
case 'One':
return One.values.byName(classValue[1]);
case 'Two':
return Two.values.byName(classValue[1]);
}
} on ArgumentError {
return null;
}
return null;
}
With the collection package you could do this:
FooEnum? getFooEnum(String string) {
return (One.values.firstWhereOrNull((e) => e.toString() == string) ??
Two.values.firstWhereOrNull((e) => e.toString() == string)) as FooEnum?;
}
I haven't looked into why the cast is needed, but it was a quick way to fix the problem that occures without it.

Just with several values?

I want a Publisher similar to Just except it should emit multiple values, and then complete the stream.
Let's call it JustSeveral:
func fibonacci(_ number: Int) -> AnyPublisher<Int, Never> {
Future { ... }.eraseToAnyPublisher()
}
JustSeveral([293, 18, 104])
.flatMap { fibonacci($0) }
.sink { print($0) }
Is there a way to combine operators with Just to get the same thing, or do I need to build this Publisher myself?
Note: Any solution should be able to handle an arbitrary number of elements in the array, not just 3.
For example, if we had an uncollect operator, this would be trivial:
Just([293, 18, 104])
.uncollect { $0 }
.flatMap { fibonacci($0) }
.sink { print($0) }
Importing Combine extends Array with a computed property called publisher, which returns a Sequence<Array<Int>, Never>, which is a Publisher. So you can just do:
[293, 18, 104]
.publisher
.flatMap { fibonacci($0) }
.sink { print($0) }
However, Apple's API documentation blows chunks, and I don't think there's a page that documents Array.publisher.
Update: You should use the built-in .publisher method.
Original answer:
One solution is to make the publisher wait until there is a subscription and then emit values:
public struct JustSeveral<Value>: Publisher {
public init(_ outputs: [Value]) {
self.outputs = outputs
}
public var outputs: [Value]
public typealias Output = Value
public typealias Failure = Never
public func receive<Downstream: Subscriber>(subscriber: Downstream) where Downstream.Input == Output, Downstream.Failure == Failure {
let subject = PassthroughSubject<Value, Never>()
subject
.subscribe(subscriber)
outputs.forEach { subject.send($0) }
subject.send(completion: .finished)
}
}
Although this works, I don't know if it's the best solution since it only emits values upon a subscription. I'm not certain that that is how Just works.

How do I set the toggle state in a foreach loop in SwiftUI

When I try to set show a toggle inside a loop of value from a dictionary, I get very little help from the error message.
If I un-comment the 3 lines of commented code below, trying to add a toggle for each of the properties in the loop, I get the following error:
Cannot convert value of type 'HStack, Text, ConditionalContent)>>' to closure result type '_'
import SwiftUI
struct properties {
var id : Int
var property : String
var isOn : Bool
}
struct ContentView: View {
#State var propertyValues: [properties] = [properties(id: 1, property: "DemoData", isOn: true),
properties(id: 2, property: "ShowLocalEvents", isOn: false)]
var body: some View {
NavigationView {
VStack {
List {
ForEach(propertyValues.identified(by: \.id)) { propertyValue in
HStack {
// Toggle(isOn: propertyValue.isOn) {
// Text("")
// }
Text("\(propertyValue.property)")
if propertyValue.isOn {
Text("On")
} else {
Text("Off")
}
}
}
}
}
}
}
}
The issue here is that the initializer Toggle(isOn:label:) takes a Binding<Bool> for its isOn parameter rather than just a Bool. A Binding<_> is sort of a readable-writable "view" into a property that allows a control to update a value that it doesn't own and have those changes propagate out to whoever does own the property.
EDIT: I made this more complicated than it needed to be. The following works:
ForEach($propertyValues.identified(by: \.id.value)) { (propertyValue: Binding<properties>) in
HStack {
Toggle(isOn: propertyValue.isOn) {
Text("")
}
// ...
}
}
By using a $propertyValues, we are accessing a Binding to the array itself, which is transformed into bindings to each of the elements.
EDIT:
For the above to work, you'll need to add .value in a couple places in the body so that you are referring to the actual values and not the bindings to the values.

How to iterate over CustomCollection generic type in Swift

I have a custom collection class with an embedded array written in Obj-c. The class implements NSFastEnumerator protocol in order to be iterable in Obj-c.
For my Swift classes I had to add the following code based on apporaches on SOF.
extension CustomCollection: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
Which again makes it iterable in Swift classes.
All is good until I need to use this class as a Generic type in one of my Swift base classes.
class SomeBaseClass<T: CustomCollection> {
typealias Collection = T
var model: Collection?
// Implementation goes here
}
When I try to iterate over my 'model' property, I get Command Signal Failure Error during build.
Any idea how this needs to be done and whether it's even possible to be done?
Running XCode 7 beta 6 and Swift 2.0
Thanks.
Here is what I came up with Xcode 7.0.1:
First the CustomCollection class. I've keep it simple since I don't know what yours is doing.
public class CustomCollection: NSFastEnumeration
{
var array: NSMutableArray = []
#objc public func countByEnumeratingWithState(state: UnsafeMutablePointer<NSFastEnumerationState>, objects buffer: AutoreleasingUnsafeMutablePointer<AnyObject?>, count len: Int) -> Int {
var index = 0
if state.memory.state != 0 {
index = Int(state.memory.state)
}
if index >= self.array.count {
return 0
}
var array = Array<AnyObject?>()
while (index < self.array.count && array.count < len)
{
array.append(self.array[index++])
}
let cArray: UnsafeMutablePointer<AnyObject?> = UnsafeMutablePointer<AnyObject?>.alloc(array.count)
cArray.initializeFrom(array)
state.memory.state = UInt(index)
state.memory.itemsPtr = AutoreleasingUnsafeMutablePointer<AnyObject?>.init(cArray)
return array.count
}
}
Then there's the code you provided.
extension CustomCollection: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
class SomeBaseClass<T: CustomCollection>
{
typealias Collection = T
var model: Collection?
}
With all this, I'm able to run the following
var myModel = CustomCollection()
myModel.array.addObject("This")
myModel.array.addObject("is")
myModel.array.addObject("a")
myModel.array.addObject(["complex", "test"])
var myVar = SomeBaseClass()
myVar.model = myModel
for myObject in myVar.model!
{
print(myObject)
}
And the console prints
This
is
a
(
complex,
test
)
Hope it helps!

Terribly Slow migrated Objc to swift code

In an application I've written I have a process that parses a large amount of data from Core-Data and displays it to a graph. While doing this processing I also end up writing the data out to a CSV File. I created a separate class called CSVLine which assists with the creation of the CSV file.
For my test case of 140k recorded my Objective-C code takes aprox 12 seconds to run. After "migrating" the class over to swift It now takes somewhere between 280-360 seconds to run. Obviously I've done something terrible.
Using Instruments I was able to identify the "slow" method and I was wondering if I've done something clear in SWIFT to cause the issue.
Objc
- (void)newLine {
// NSLog(#"Appending %#", self.csvString);
[outData appendData:[self csvData] ];
[self clear];
}
- (void)clear {
// Erase every single value
for (NSUInteger i = 0; i < [values count]; i ++) {
values[i] = #"";
}
}
Swift
func newLine() {
outData.appendData(csvData())
clear()
}
// Clear out the Array
func clear() {
for (var i = 0; i < values.count; i++) {
values[i] = ""
}
}
I'm working with various types of data that are all being written to a single CSV file so there are many blank lines. To accommodate for this I designed this class so that it has an array of keys and an array of values. The keys store the "column" names for the CSV file and values will store either a blank or a value for the index of the key for that data element.
Example:
Keys = [speed,heading,lat,lon]
values might be [200,300,"",""]
or ["","","38.553","25.2256"]
Once I'm done with a line i will write a comma joined list of the values into an internal data structure and clear out the line (erase all the items in the values array). This seems to be where the slowdown is with the swift class. Is there something blatantly "slow" i'm doing when i zero out my array?
Full Swift Class
#objc class CSVLineSwift : NSObject {
// Define Arrays
var keys: [String] = [String]()
var values: [String] = [String]()
var outData : NSMutableData = NSMutableData()
override init() {
}
// Singelton Operator - Thread Safe :: http://code.martinrue.com/posts/the-singleton-pattern-in-swift
class var instance : CSVLineSwift {
// Computed Property
struct Static {
static var instance : CSVLineSwift?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = CSVLineSwift();
}
return Static.instance!
}
// Erase existing Data
func newFile() {
outData = NSMutableData();
outData.appendData(headerData())
}
func csvString() -> String {
return ",".join(values)
}
func csvData() -> NSData {
let string = csvString()
let data = string.dataUsingEncoding(NSUTF8StringEncoding)
return data!
}
func addField(field : String) {
keys.append(field)
values.append("")
}
func setValueForKey(value:String, key:String) {
if let index = find(keys, key) {
values[index] = value
} else {
print( "ERROR -- There was no key: \(key) in the header Array")
}
}
func headerString() -> String {
return ",".join(keys)
}
func headerData() -> NSData {
return headerString().dataUsingEncoding(NSUTF8StringEncoding)!
}
func newLine() {
outData.appendData(csvData())
clear()
}
// Clear out the Array
func clear() {
for (var i = 0; i < values.count; i++) {
values[i] = ""
}
}
func writeToFile(fileName : String) {
outData.writeToFile(fileName, atomically: true)
}
}
Be sure that Swift's optimization level in Build Settings is not -Onone. In my experience, it is orders of magnitude slower than -O. (-O is also the default for 'release', so alternatively you could simply build for release, as already suggested.) As for 'zeroing out' the array, it might be faster (although I do not know) to simply re-initialize the array with a repeated value:
values = [String](count: values.count, repeatedValue: "")
Or if you know you will be appending the new values as you go along, and are not bound to using the indices, you could call:
values.removeAll(keepCapacity: true)
And add the new values with values.append() rather than at indices.
It appears my issues were due to a debug build. In debug build there is no optimization. Once you do a run build the optimization kicks in and things run MUCH MUCH faster....
Similar to the answer posted here: Is Swift really slow at dealing with numbers?

Resources