Returning Array from Function (Swift) - ios

This is my first question so please, be gentle!
I've been debugging my code and searching stackexchange.com for a while, and as best as I can tell, my problem has to do with return an array from a function. I can assure you that this is not a homework question, and I've tried going through Stackoverflow for solutions, and I'm at my wits end!
The ActiveModelResponseList is used to display a list of acceptable responses to the user, arranged in a List form:
struct ActiveModelResponseList: View {
var activeModel: ActiveModel
var body: some View {
List(activeModel.validResponses()) { /* XCode error: "Cannot convert value of type
'[ValidResponse]' to expected
argument type 'Range<Int>'" */
HStack { /* XCode error: "Contextual closure type '() -> TupleView<(Text, Text, Spacer)>'
expects 0 arguments, but 1 was used in closure body" */
Text($0.response)
Text($0.narrative)
Spacer()
}.padding(10)
}
}
}
I have tried a number of different ways to restructure the above body, and it's specifically that activeModel.validResponses() that causes the errors. If I delete it and populate the list with hard coded values, it works just fine.
That function, activeModel.validResponses() comes from the ActiveModel class, as follows:
class ActiveModel {
var baseModel: ModelData
private var responses: [Int]
private var activeElement: Int
// Although forThisElement is unused in the base function, ActiveModel still has
// such an element, used for other purposes
public func validResponses() -> [ValidResponse] {
return (baseModel.validResponses(forThisElement: activeElement))
}
}
This, in turn, comes from a base class, ModelData. Note that forThisElement is not actually required for this function, but is included to maintain polymorphism (i.e. other models will use it). As you can see, ModelData.validResponses returns a [ValidResponse]
class ModelData: Hashable, Codable, Identifiable {
var id: Int
var valid_response: [String]
var valid_response_narrative: [String]
public func validResponses(forThisElement: Int) -> [ValidResponse] {
// forThisElement is currently an unused input variable,
// but is required for compatibility with other classes
var thisResult: [ValidResponse] = []
for thisResponse in 0..<valid_response.count {
thisResult[thisResponse].id = thisResponse
thisResult[thisResponse].response = valid_response[thisResponse]
thisResult[thisResponse].narrative = valid_response_narrative[thisResponse]
}
return thisResult
}
}
The ValidResponse is just an ordinary struct, defined as follows:
struct ValidResponse: Identifiable, Hashable {
var id: Int = 0
var response: String = "0"
var narrative: String = "Default"
}
The preview is being generated for an iPod touch (7th generation), I'm using Xcode Version 13.1 (13A1030d), and am compiling using MacOS 12.0.1, on a mid-2015 MacBook Pro.
I found the following answers on stackexchange, but I feel none the wiser after having read them (and, as these answers are quite dated, I wonder if they're still relevant):
returning an array from a function in Swift
Return Array from Function in Swift
Square every element in an Int of arrays. (Swift)
Can anyone provide any guidance?

The first error message is nonsense. Fix the second and it will disappear.
The title of your question is unrelated to your actual problem, which is that you're trying to use a positional parameter from an outer closure. That doesn't work; they can't propagate that way. Every new closure scope gets its own set of positional parameters, and HStack.init doesn't use any, as the error tells you.
Fix: name the argument.
List(activeModel.validResponses()) { response in
HStack {
Text(response.response)
Text(response.narrative)

Related

Swift: How to deal with method overloads where only the return type differs and the result is discardable?

this is my first question and I hope you guys can help me out.
Lets assume I've written a Framework in Swift that does stuff purely with native Swift Types. This is our interface:
class Foo {
#discardableResult
public func perform(query: String) -> [String: Any]? {
return [:]
}
}
Note that our result is discardable.
I would now like to support other Libraries optionally. I know we have Codable and stuff but lets assume that I want to support SwiftyJSON optionally by creating an extension that only compiles when SwiftyJSON can be imported.
#if canImport(SwiftyJSON)
import SwiftyJSON
extension Foo {
#discardableResult
func perform(query: String) -> JSON? {
// Call internal func => cast JSON => return JSON
return JSON()
}
}
#endif
Without SwiftyJSON (can not import) this compiles:
let bar = Foo().perform(query: "")
With SwiftyJSON it does not because its ambiguous.
Return type has to be explicitly defined like:
let baz: [String: Any]? = Foo().perform(query: "")
// or
let bar: JSON? = Foo().perform(query: "")
For calls where I want the result thats fine.
BUT: Since we have a discardableResult calls like
Foo().perform(query: "")
will always be ambiguous.
The following calls work but are not very nice imho.
Foo().perform(query: "") as JSON?
Foo().perform(query: "") as [String: Any]?
What is the best way to deal with this problem? Should the method just not be overloaded and have a different name instead? Or am I overlooking something that makes the calls non ambiguous in a nice way?
Edit:
A comment suggested to remove discardableResult. I really do not wanna do that since that would lead to a lot of calls that look like this:
_ = Foo().perform(query: "")
The more I think about the problem it occurs to me that there might just not be a good solution for this..
Actually, removing #discardableResult wouldn't work, and _ = Foo().perform(query: "") wouldn't compile, it would still have the same ambiguity: it's not that the compiler is being nitpicky, but it has literally no way of knowing which of your two functions to call!
It might be interesting to think about what behavior you'd expect from this line, and why.
You seem to want to have a function based on an existing function which:
Returns a different type;
Has the same name and parameters;
Has a discardable result.
Unfortunately, it looks like it's a classic "pick any two" type of scenario…
Well, let's do just that! Let's see what happens if we stop insisting on any one of the three conditions.
Let me use a simpler example:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomething() -> Int {
return 1
}
doSomething() // won't compile
let x:String
x = doSomething() // "a"
We have a function overloaded with two return types (String and Int), and we see that we cannot discard its result as it would result in unresolvable ambiguity.
1. Single return type: use an enum with a payload
Let's eliminate the first condition, and try using a single return type. This is possible:
enum ReturnType {
case text(_ payload:String)
case number(_ payload:Int)
}
#discardableResult func doSomething() -> ReturnType {
if ... // some condition
{
return .text("a")
} else {
return .number(1)
}
}
doSomething() // works
let x = doSomething() // ReturnType.text("a") or ReturnType.number(1)
Here we cannot (easily) extend the functionality via extensions; rather, we'd need to touch the enum and function code with all the possible options every time we want to add a new return type. (Of course advanced APIs can also be created that help third parties easily write extensions… If it's worth the effort.)
We would also need a way to determine which return type to choose: it can be a function parameter, or a value stored elsewhere, etc. Here's an example with a parameter:
#discardableResult func doSomething(useText:Bool = false) -> ReturnType {
if useText {
return .text("a")
} else {
return .number(1)
}
}
doSomething() // works
doSomething(useText:true) // works
let x = doSomething() // ReturnType.number(1)
let x2 = doSomething(useText:false) // ReturnType.number(1)
let x3 = doSomething(useText:true) // ReturnType.text("a")
Note: Here, we lose the convenience of having the compiler infer the type from the call site; since the type is now an opaque wrapper, it's no longer the compiler's business to make sure that the wrapped type is correct. This may be a high cost for simply maintaining "result discardability," but then it may also allow us to abstract away some details, only unwrapping the "payload" when needed, which can have its own benefits.
2. Change the name or parameters of the function
This is quite easy:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomethingElse() -> Int {
return 1
}
doSomething() // works
doSomethingElse() // works
let x = doSomething() // "a"
let y = doSomethingElse() // 1
We can also use this in an extension. All we lose is the shared name.
Changing the parameters is also possible but it would be pretty silly in this already-silly example:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomething(thinkOfAnyNumber:Int) -> Int {
return 1
}
doSomething() // "a"
doSomething(thinkOfAnyNumber:42) // 1
Note: I'd obviously only do this if the additional parameters actually make sense.
Note 2: The parameter configuration is different between the two functions, not the parameter values like in the previous case.
3. Do not make the result discardable
Note that simply removing the #discardableResult attribute won't make it impossible to try to discard the result: you'll still be able to try the _ = assignment, or simply ignore the function result (and the compiler warning). Both will ultimately fail to compile, so it will be up to the user of the API to avoid doing either.
Hopefully, your function does some other things (side effects) than provide a return value: in that case, there may be little use in discarding the result of a function that does nothing but provide that result, and one may probably be better off not calling it in the first place.
If the side effects are identical between the two overloaded functions, you can factor them out into a single function:
func doTheActualWork() {
// ...
}
func doSomething() -> String {
doTheActualWork()
return "a"
}
func doSomething() -> Int {
doTheActualWork()
return 1
}
doSomething() // won't compile
doTheActualWork() // use this instead
let z:String = doSomething() // "a"
Note that this can also be done via extensions as long as they re-use existing functions and only overload them with different signatures and/or return types.
Now if the side effects (i.e. doTheActualWork() implementations) are also different in the two cases… Then I give up. But that can be handled as well.
Well, this is what I've been able to gather on this interesting problem. Now I may be dead wrong, and there may be something better out there… We'll find out soon enough.

Property Wrapper for CurrentValueSubject - memory management

I would like to create a property wrapper for CurrentValueSubject. I have done this like that:
#propertyWrapper
public class CurrentValue<Value> {
public var wrappedValue: Value {
get { projectedValue.value }
set { projectedValue.value = newValue }
}
public var projectedValue: CurrentValueSubject<Value, Never>
public init(wrappedValue: Value) {
self.projectedValue = CurrentValueSubject(wrappedValue)
}
}
This works but there is a little thing I would like to change with it - use struct instead of class. The problem with using struct for this is that sometimes I could get Simultaneous accesses error. And I know why, this happens when in sink from this publisher I would try to read the value from wrapped value. So for example with code like this:
#CurrentValue
let test = 1
$test.sink { _ in
print(self.test)
}
And I more or less know why - because when projectedValue executes its observation, wrapped value is still in process of setting its value. In class this is ok, because it would just change the value, but with struct it actually modifies the struct itself, so Im trying to write and read from it at the same time.
My question is - is there some clever way to overcome this, while still using struct? I don't want to dispatch async.
Also I know that #Projected works similarly to this propertyWrapper, but there is a big difference - Projected executes on willSet, while CurrentValueSubject on didSet. And Projected has the same issue anyway.
I know that I can read the value inside the closure, but sometimes Im using this with various function calls, that might eventually use self.test instead.
Try my implementation
#propertyWrapper
class PublishedSubject<T> {
var wrappedValue: T {
didSet {
subject.send(wrappedValue)
}
}
var projectedValue: some Publisher<T, Never> {
subject
}
private lazy var subject = CurrentValueSubject<T, Never>(wrappedValue)
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}

Swift extension type constraints

XCode 11.2.1
macOS Mojave 10.14.6
So, I'm trying to add the ability to remove objects from an array, if I've kept the reference. Following internet advice, I've caused my protocol to inherit from AnyObject, as this causes/requires/implies === to be defined on any classes that implement the protocol. However, XCode is behaving strangely with the types on the Array extension. Consider that the following compiles fine:
public protocol Foo: AnyObject {
}
public extension Array where Element == Foo {
mutating func removeElement(element: Element) {
if let idx = self.firstIndex(where: {$0 === element}) {
self.remove(at: idx)
}
}
}
public func bar(array: [Foo], element: Foo) -> [Foo] {
var arrayCopy: [Foo] = array
arrayCopy.removeElement(element: element)
return arrayCopy
}
but that if I change the extension type to Element: AnyObject, I get a compile error as follows:
...
public extension Array where Element: AnyObject {
...
...
// Compiler error: '[Foo]' requires that 'Foo' conform to 'AnyObject'
arrayCopy.removeElement(element: element)
...
Foo DOES conform to AnyObject. It's right there in its definition. Why does XCode not acknowledge this?
You are using old software so you don't get the modern error message:
Referencing instance method 'removeElement(element:)' on 'Array'
requires that 'Foo' be a class type
And that's true. element isn't an instance of a static type, as far as the compiler understands, but it will be if you write it like this instead:
public func bar<Foo: Module.Foo>(array: [Foo], element: Foo) -> [Foo] {
where Module is the actual name of your module, of course.
If you need to use a heterogeneous array, then I don't know how to make things any cleaner than this. Let me know if you figure out better.
(Keep the signature of the function the same; change the body instead.)
var arrayCopy: [AnyObject] = array
arrayCopy.removeElement(element: element)
return arrayCopy as! [Foo]

Associated type error: Cannot assign a value of type '[SpecificType]' to a value of type '[NameOfTypeAlias]'

I am trying to create a fairly generic UITableView implementation that can display different types of cells. I'm using associated types to specify the data source type, cell type, and so on. I have most of it working really well, but I am having some trouble subclassing the implementation. I'm showing the least amount of code I can below to get the point across.
Here's my high-level architecture:
protocol DGTableViewAble {
typealias DGTableViewItemType
...
var items: [DGTableViewItemType] { get set }
}
class DGTableView: UITableView, DGTableViewAble {
typealias DGTableViewItemType = User
var items: [DGTableViewItemType] = [] { ... }
}
class DGPostsTableView: DGTableView {
typealias DGTableViewItemType = Post
}
...
Things work great when I assign an array of User objects to any DGTableView instance. For example, this works great:
var users: [User] = [...]
var userTableView: DGTableView
userTableView.items = users
However, when I try this:
var posts: [Post] = [...]
var postsTableView: DGPostsTableView
postsTableView.items = posts
I get the error:
Cannot assign a value of type '[Post]' to a value of type '[DGTableViewItemType]'
It seems like the compiler has trouble determining the associated types as I have things set up. Any ideas why? Any suggestions on improving the architecture?
You're not inheriting from DGTableViewAble in your class interface for DGPostsTableView:
class DGPostsTableView: DGTableView, DGTableViewAble {
typealias DGTableViewItemType = Post
...
}
Post isn't declaring viewable protocol?
class DGPostsTableView: DGTableView, DGTableViewAble {
typealias DGTableViewItemType = Post
}
It will also have to conform to said protocol. Maybe marking the unneeded protocol components as optional could solve your issue?

How to use generic protocol as a variable type

Let's say I have a protocol :
public protocol Printable {
typealias T
func Print(val:T)
}
And here is the implementation
class Printer<T> : Printable {
func Print(val: T) {
println(val)
}
}
My expectation was that I must be able to use Printable variable to print values like this :
let p:Printable = Printer<Int>()
p.Print(67)
Compiler is complaining with this error :
"protocol 'Printable' can only be used as a generic constraint because
it has Self or associated type requirements"
Am I doing something wrong ? Anyway to fix this ?
**EDIT :** Adding similar code that works in C#
public interface IPrintable<T>
{
void Print(T val);
}
public class Printer<T> : IPrintable<T>
{
public void Print(T val)
{
Console.WriteLine(val);
}
}
//.... inside Main
.....
IPrintable<int> p = new Printer<int>();
p.Print(67)
EDIT 2: Real world example of what I want. Note that this will not compile, but presents what I want to achieve.
protocol Printable
{
func Print()
}
protocol CollectionType<T where T:Printable> : SequenceType
{
.....
/// here goes implementation
.....
}
public class Collection<T where T:Printable> : CollectionType<T>
{
......
}
let col:CollectionType<Int> = SomeFunctiionThatReturnsIntCollection()
for item in col {
item.Print()
}
As Thomas points out, you can declare your variable by not giving a type at all (or you could explicitly give it as type Printer<Int>. But here's an explanation of why you can't have a type of the Printable protocol.
You can't treat protocols with associated types like regular protocols and declare them as standalone variable types. To think about why, consider this scenario. Suppose you declared a protocol for storing some arbitrary type and then fetching it back:
// a general protocol that allows for storing and retrieving
// a specific type (as defined by a Stored typealias
protocol StoringType {
typealias Stored
init(_ value: Stored)
func getStored() -> Stored
}
// An implementation that stores Ints
struct IntStorer: StoringType {
typealias Stored = Int
private let _stored: Int
init(_ value: Int) { _stored = value }
func getStored() -> Int { return _stored }
}
// An implementation that stores Strings
struct StringStorer: StoringType {
typealias Stored = String
private let _stored: String
init(_ value: String) { _stored = value }
func getStored() -> String { return _stored }
}
let intStorer = IntStorer(5)
intStorer.getStored() // returns 5
let stringStorer = StringStorer("five")
stringStorer.getStored() // returns "five"
OK, so far so good.
Now, the main reason you would have a type of a variable be a protocol a type implements, rather than the actual type, is so that you can assign different kinds of object that all conform to that protocol to the same variable, and get polymorphic behavior at runtime depending on what the object actually is.
But you can't do this if the protocol has an associated type. How would the following code work in practice?
// as you've seen this won't compile because
// StoringType has an associated type.
// randomly assign either a string or int storer to someStorer:
var someStorer: StoringType =
arc4random()%2 == 0 ? intStorer : stringStorer
let x = someStorer.getStored()
In the above code, what would the type of x be? An Int? Or a String? In Swift, all types must be fixed at compile time. A function cannot dynamically shift from returning one type to another based on factors determined at runtime.
Instead, you can only use StoredType as a generic constraint. Suppose you wanted to print out any kind of stored type. You could write a function like this:
func printStoredValue<S: StoringType>(storer: S) {
let x = storer.getStored()
println(x)
}
printStoredValue(intStorer)
printStoredValue(stringStorer)
This is OK, because at compile time, it's as if the compiler writes out two versions of printStoredValue: one for Ints, and one for Strings. Within those two versions, x is known to be of a specific type.
There is one more solution that hasn't been mentioned on this question, which is using a technique called type erasure. To achieve an abstract interface for a generic protocol, create a class or struct that wraps an object or struct that conforms to the protocol. The wrapper class, usually named 'Any{protocol name}', itself conforms to the protocol and implements its functions by forwarding all calls to the internal object. Try the example below in a playground:
import Foundation
public protocol Printer {
typealias T
func print(val:T)
}
struct AnyPrinter<U>: Printer {
typealias T = U
private let _print: U -> ()
init<Base: Printer where Base.T == U>(base : Base) {
_print = base.print
}
func print(val: T) {
_print(val)
}
}
struct NSLogger<U>: Printer {
typealias T = U
func print(val: T) {
NSLog("\(val)")
}
}
let nsLogger = NSLogger<Int>()
let printer = AnyPrinter(base: nsLogger)
printer.print(5) // prints 5
The type of printer is known to be AnyPrinter<Int> and can be used to abstract any possible implementation of the Printer protocol. While AnyPrinter is not technically abstract, it's implementation is just a fall through to a real implementing type, and can be used to decouple implementing types from the types using them.
One thing to note is that AnyPrinter does not have to explicitly retain the base instance. In fact, we can't since we can't declare AnyPrinter to have a Printer<T> property. Instead, we get a function pointer _print to base's print function. Calling base.print without invoking it returns a function where base is curried as the self variable, and is thusly retained for future invocations.
Another thing to keep in mind is that this solution is essentially another layer of dynamic dispatch which means a slight hit on performance. Also, the type erasing instance requires extra memory on top of the underlying instance. For these reasons, type erasure is not a cost free abstraction.
Obviously there is some work to set up type erasure, but it can be very useful if generic protocol abstraction is needed. This pattern is found in the swift standard library with types like AnySequence. Further reading: http://robnapier.net/erasure
BONUS:
If you decide you want to inject the same implementation of Printer everywhere, you can provide a convenience initializer for AnyPrinter which injects that type.
extension AnyPrinter {
convenience init() {
let nsLogger = NSLogger<T>()
self.init(base: nsLogger)
}
}
let printer = AnyPrinter<Int>()
printer.print(10) //prints 10 with NSLog
This can be an easy and DRY way to express dependency injections for protocols that you use across your app.
Addressing your updated use case:
(btw Printable is already a standard Swift protocol so you’d probably want to pick a different name to avoid confusion)
To enforce specific restrictions on protocol implementors, you can constrain the protocol's typealias. So to create your protocol collection that requires the elements to be printable:
// because of how how collections are structured in the Swift std lib,
// you’d first need to create a PrintableGeneratorType, which would be
// a constrained version of GeneratorType
protocol PrintableGeneratorType: GeneratorType {
// require elements to be printable:
typealias Element: Printable
}
// then have the collection require a printable generator
protocol PrintableCollectionType: CollectionType {
typealias Generator: PrintableGenerator
}
Now if you wanted to implement a collection that could only contain printable elements:
struct MyPrintableCollection<T: Printable>: PrintableCollectionType {
typealias Generator = IndexingGenerator<T>
// etc...
}
However, this is probably of little actual utility, since you can’t constrain existing Swift collection structs like that, only ones you implement.
Instead, you should create generic functions that constrain their input to collections containing printable elements.
func printCollection
<C: CollectionType where C.Generator.Element: Printable>
(source: C) {
for x in source {
x.print()
}
}

Resources