What Is a Good Alternative for Static Stored Properties of Generic Types in Swift

How to define static constant in a generic class in swift?

You can define global constant with fileprivate or private access level in the same .swift file where your generic class is defined. So it will not be visible outside of this file and will not pollute global (module) namespace.

If you need to access this constant from outside of current file then declare it as internal (default access level) or public and name it like ClassConstant so it will be obvious that it relates to Class.

Read more about access levels in Swift 3.

Extension may not contain stored property but why is static allowed

Extensions cannot contain stored instance properties. Why? Because adding an instance property would change the size of instances of that type. What happens if one module adds an extension such that an Int is now 2 words long? What should then happen when it, for example, gets an Int from another module where they are still 1 word in size?

The reason why static stored properties are permitted in extensions is simply because they have static lifetime; they exist independently of any instances of the given type you're extending. Really they're nothing more than global stored variables, just namespaced to a type. Therefore they can be freely added without affecting code that has already been compiled without knowledge of them.

It's worth noting however that there are currently three restrictions on defining static stored properties.

1. You cannot define a static stored property on a generic type

This would require separate property storage for each individual specialisation of the generic placeholder(s). For example, with:

struct S<T> {

static var foo: Int {
return 5
}

static let bar = "" // error: Static stored properties not supported in generic types
}

Just as foo is called on individual specialisation of S, e.g S<Int>.foo and S<Float>.foo and not on S itself (in fact; S is not even a type currently, it requires that T be satisfied); bar would (likely) be the same. It would be called as, for example, S<Int>.bar, not S.bar.

This is an important detail because the metatype that a static member is called on is passed to the receiver as the implicit self argument. This is accessible in static property initialiser expressions; therefore allowing them to call other static methods.

Therefore being able to call the same static property initialiser on different specialisations of a generic type would have the potential to create different property values for each (consider the simple case of static let baz = T.self). Therefore we need separate storage for each of them.

However, that all being said, there's no real reason why the compiler/runtime cannot do this, and it may well do in a future version of the language. Although one argument against this is that it may produce confusing behaviour in some cases.

For example, consider:

import Foundation

struct S<T> {
static let date = Date()
}

If the runtime implicitly generated new storage for date each time it gets accessed on a new specialisation of S<T>, then S<Float>.date would not equal S<Int>.date; which may be confusing and/or undesirable.

2. You cannot define a static stored property in a protocol extension

This mostly follows on from the previous point. A static stored property in a protocol extension would require separate storage for each conforming type of that protocol (but again; there's no reason why the compiler/runtime cannot do this).

This is necessary with protocols, as static members in protocol extensions are not members on the protocol type itself. They are members on concrete types that conform to the protocol.

For example, if we have:

protocol P {}

extension P {

static var foo: Int {
return 5
}

static let bar = "" // error: Static stored properties not supported in generic types
// (not really a great diagnostic)
}

struct S : P {}
struct S1 : P {}

We cannot access foo on the protocol type itself, we cannot say P.foo. We can only say S.foo or S1.foo. This is important because foo's getter can call out to static protocol requirements on self; however this isn't possible if self is P.self (i.e the protocol type itself), as protocols don't conform to themselves.

The same would (likely) follow for static stored properties such as bar.

3. You cannot define a class stored property

I don't believe there would be any problems with such a declaration in the class body itself (it would simply be equivalent to a computed class property backed by a static stored property).

However it would be potentially problematic in extensions, because extensions cannot add new members to a Swift class vtable (though they can add to the Obj-C counterpart if applicable). Therefore in most cases they wouldn't be dynamically dispatched to (so would effectively be final, and therefore static). Although that being said, class computed properties are currently permitted in extensions, so it may be permissible in the interests of consistency.

Swift, use generic property with different specific types - Reference to generic type requires arguments in

You can specify a protocol for providing Stage types like so:

protocol StageProvider {

associatedtype T: Stage

func getType() -> T.Type

}

Then make your SomethingThatKnowsAboutTheStages or any other one conform this protocol:

class SomethingThatKnowsAboutTheStages: StageProvider {

typealias T = SecondStage

func getType() -> T.Type {
SecondStage.self
}

}

Add an initializer for your FooBarController:

class FooBarController<StageType: Stage>: UIViewController {

convenience init(stage: StageType.Type) {
self.init()
}

}

And finally use all these:

func fooBarScreen<T: StageProvider>(boop: T) {
let controller = FooBarController(stage: boop.getType())
}

How to store generic type to avoid internal struct requiring it in Swift?

Here is possible approach (however I'd keep it outside, like ButtonStyle is outside of Button)... anyway, here it is:

public struct Example {
private let content: AnyView

init<Content: View>(@ViewBuilder content: @escaping () -> Content) {
self.content = AnyView(content())
}

func contentView() -> some View {
self.content
}

public struct ActionKey: Hashable, Equatable, RawRepresentable {
public static let cancelButtonClicked = ActionKey("cancelButtonClicked") // Static stored properties not supported in generic types

public static func == (lhs: ActionKey, rhs: ActionKey) -> Bool {
return lhs.rawValue == rhs.rawValue
}

public let rawValue: String

public init(rawValue: String) {
self.rawValue = rawValue
}

public init(_ rawValue: String) {
self.init(rawValue: rawValue)
}
}
}

How to add type methods to generic types in Swift?

When encountering an issue with generics that doesn’t work the way expect it’s often useful, to help understand the issue, to think about what would happen if you replaced a placeholder with different concrete types.

So for example, suppose you defined the following (this won’t compile for the reason you give, but imagine it did):

class C<T> {
static var a: [T] = []

static func addElement(i: T) {
a.append(i)
}
}

Really, when you write class C<T>, you aren’t writing a class. You’re writing a blueprint for an infinite number of possible classes – C<Int>, C<String>, C<AnythingElse>.

Under these circumstances, suppose you wrote C.addElement(1) – so T would be Int. Then later, you wrote C.addElement("one"). Would C.a now contain one item, or two? What would the type of a be?

Probably the most reasonable answer would be that there would be a C<Int>.a with one element, and a C<String>.a with one element. But this could get very confusing, and it doesn’t seem like there are use cases to justify this kind of functionality.

Generally, static methods and properties are best used rarely, and you may find you are better off with non-static members and methods, combined with a singleton pattern (or maybe not even that). If you still find the need for static-like functions, you may be better off using a generic free function (e.g. func emptyArchive<T: C>(c: C) { }).

How to make a singleton for a generic class in Swift

The fundamental problem here is that you're creating a whole family of singletons (one for each parameterized type) and each singleton needs to be stored (or referenced from) the static context. And the singletons need to be indexed by the type that they store.

I suggest creating a single, global dictionary in which to store your singletons, indexed by string descriptions of their types:

var singletons_store : [String, AnyObject]

Then in your computed shared variable you look in that store for the singleton that corresponds to a parameterized type:

class var shared : APIClient<T> {
let store_key = String(describing: T.self)
if let singleton = singletons_store[store_key] {
return singleton as! APIClient<T>
} else {
let new_singleton = APIClient<T>()
singleton_store[store_key] = new_singleton
return new_singleton
}
}

What is the proper way to reference a static variable on a Swift Protocol?

In the example you show, there is no difference. Because identifier is a protocol requirement, it will be dynamically dispatched to in both cases, therefore you don't need to worry about the wrong implementation being called.

However, one difference arises when you consider the value of self inside the static computed property when classes conform to your protocol.

self in a static method/computed property is the metatype value that it's is called on. Therefore when called on I, self will be I.self – which is the static type that the compiler infers the generic placeholder I to be. When called on type(of: identifiable), self will be the dynamic metatype value for the identifiable instance.

In order to illustrate this difference, consider the following example:

protocol Identifiable {
static var identifier: String { get }
}

extension Identifiable {
static var identifier: String { return "\(self)" }
}

func work<I : Identifiable>(on identifiable: I) {
let identifier = I.identifier
print("from Protocol: \(identifier)")

let identiferFromType = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}

class C : Identifiable {}
class D : C {}

let d: C = D()

// 'I' inferred to be 'C', 'type(of: d)' is 'D.self'.
work(on: d)

// from Protocol: C
// using type(of:): D

In this case, "which is better" completely depends on the behaviour you want – static or dynamic.

C#'s IEqualityComparer TType similar or alternative in Swift Language

That would be Equatable protocol:

https://developer.apple.com/documentation/swift/equatable

Type Erasure for more complex protocols

Interestingly, I built something not unlike this earlier this year for a commercial project. It's challenging to do in a general way, but most of the problems come from backwards thinking. "Start with the end in mind."

// I want to be able to filter a sequence like this:
let newArray = myArray.filteredBy([
MyModel.Filters.DueDateFilter(selectedOptions: [.in24hours(past: false)]),
MyModel.Filters.StatusFilter(selectedOptions: [.a, .b])
])

This part is very straightforward. It doesn't even require filteredBy. Just add .filter to each element:

let newArray = myArray
.filter(MyModel.Filters.DueDateFilter(selectedOptions: [.in24hours(past: false)]).filter)
.filter(MyModel.Filters.StatusFilter(selectedOptions: [.a, .b]).filter)

If you wanted to, you can write filtered by this way, and do the same thing:

func filteredBy(_ filters: [(Element) -> Bool]) -> [Element] {...}

The point is that Filter here isn't really a "filter." It's a description of a filter, with lots of other things attached about the UI (we'll talk about that more later). To actually filter, all you need is (Element) -> Bool.

What we really want here is a way to build up a ([Element]) -> Element with a nice, expressive syntax. In a functional language, that'd be pretty straightforward because we'd have things like partial application and functional composition. But Swift doesn't really like doing those things, so to make it prettier, let's build some structs.

struct Filter<Element> {
let isIncluded: (Element) -> Bool
}

struct Map<Input, Output> {
let transform: (Input) -> Output
}

We'll need a way to get started, so let's use an identity map

extension Map where Input == Output {
init(on: Input.Type) { transform = { $0 }}
}

And we'll want a way to think about keyPaths

extension Map {
func keyPath<ChildOutput>(_ keyPath: KeyPath<Input, ChildOutput>) -> Map<Input, ChildOutput> {
return Map<Input, ChildOutput>(transform: { $0[keyPath: keyPath] })
}
}

And finally we'll want to create an actual filter

extension Map {
func inRange<RE: RangeExpression>(_ range: RE) -> Filter<Input> where RE.Bound == Output {
let transform = self.transform
return Filter(isIncluded: { range.contains(transform($0)) })
}
}

Add a helper for "last 24 hours"

extension Range where Bound == Date {
static var last24Hours: Range<Date> { return Date(timeIntervalSinceNow: -24*60*60)..<Date() }
}

And now we can build a filter that looks like:

let filters = [Map(on: MyModel.self).keyPath(\.dueDate).inRange(Range.last24Hours)]

filters is of type Filter<MyModel>, so any other thing that filters MyModel is legitimate here. Tweaking your filteredBy:

extension Sequence {
func filteredBy(_ filters: [Filter<Element>]) -> [Element] {
return filter{ element in filters.allSatisfy{ $0.isIncluded(element) } }
}
}

OK, that's the filtering step. But your problem is also basically "UI configuration" and for that you want to capture a lot more elements than this does.

But your example usage won't get you there:

// Also I want to be able to save the state of all filters like this
var activeFilters: [AnyFilter] = [ // ???
MyModel.Filters.DueDateFilter(selectedOptions: [.in24hours(past: false)]),
MyModel.Filters.StatusFilter(selectedOptions: [.a, .b])
]

How can you transform AnyFilter into UI elements? Your filter protocol allows literally any option type. How would you display the UI for that if the option type were OutputStream or DispatchQueue? The type you've created doesn't address the problem.

Here's one way to go about it. Create a FilterComponent struct that defines the needed UI elements and provides a way to construct a filter.

struct FilterComponent<Model> {
let optionTitles: [String]
let allowsMultipleSelection: Bool
var selectedOptions: IndexSet
let makeFilter: (IndexSet) -> Filter<Model>
}

Then to create a date filter component, we need some options for dates.

enum DateOptions: String, CaseIterable {
case inPast24hours = "In the past 24 hours"
case inNext24hours = "In the next 24 hours"

var dateRange: Range<Date> {
switch self {
case .inPast24hours: return Date(timeIntervalSinceNow: -24*60*60)..<Date()
case .inNext24hours: return Date()..<Date(timeIntervalSinceNow: -24*60*60)
}
}
}

And then we want a way to create such a component with the correct makeFilter:

extension FilterComponent {
static func byDate(ofField keyPath: KeyPath<Model, Date>) -> FilterComponent<Model> {
return FilterComponent(optionTitles: DateOptions.allCases.map{ $0.rawValue },
allowsMultipleSelection: false,
selectedOptions: [],
makeFilter: { indexSet in
guard let index = indexSet.first else {
return Filter<Model> { _ in true }
}
let range = DateOptions.allCases[index].dateRange
return Map(on: Model.self).keyPath(keyPath).inRange(range)
})
}
}

With all that, we can create components of type FilterComponent<MyModel>. No internal types (like Date) have to be exposed. No protocols needed.

let components = [FilterComponent.byDate(ofField: \MyModel.dueDate)]


Related Topics



Leave a reply



Submit