Referring to properties of containing class when using internal structs in swift - swift

I'm refactoring a project to use MVVM and using protocols to ensure that my view models have a consistent structure. This works fine for defining public properties relating to input and output (which are based on internal structs) but defining actions in the same way is proving problemmatic as, currently, they are defined as closures which have to refer to view model properties. If I use the same approach as I have to input and output properties, I don't think I can access properties of the containing instance.
Example:
protocol ViewModelType {
associatedtype Input
associatedtype Output
associatedtype Action
}
final class MyViewModel: ViewModelType {
struct Input { var test: String }
struct Output { var result: String }
struct Action {
lazy var createMyAction: Action<String, Void> = { ... closure to generate Action which uses a MyViewModel property }
}
var input: Input
var output: Output
var action: Action
}
It's not a deal breaker if I can't do it, but I was curious as I can't see any way of getting access to the parent's properties.

Answer to your question
Let's begin with a note that createMyAction: Action<String, Void> refers to the type (struct) named Action as if it was a generic, but you have not declared it as such and will thus not work.
And to answer your question of the nested struct Action can refer its outer class MyViewModel - yes you can refer static properties, like this:
struct Foo {
struct Bar {
let biz = Foo.buz
}
static let buz = "buz"
}
let foobar = Foo.Bar()
print(foobar.biz)
But you should probably avoid such circular references. And I will omit any ugly hack that might be able to achive such a circular reference on non static properties (would probably involve mutable optional types). It is a code smell.
Suggestion for MVVM
Sounds like you would like to declare Action as a function? I'm using this protocol myself:
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
Originally inspired by SergDort's CleanArchitecture.
You can prepare an instance of input (containing Observables) from the UIViewController and call the transform function and then map the Output of transform (being Observabless) to update the GUI.
So this code assumes you have basic Reactive knowledge. As for Observables you can chose between RxSwift or ReactiveSwift - yes their names are similar.
If you are comfortable with Rx, it is an excellent way of achieving a nice MVVM architecture with simple async updates of the GUI. In the example below, you will find the type Driver which is documented here, but the short explanation is that is what you want to use for input from views and input to views, since it updates the views on the GUI thread and it is guaranteed to not error out.
CleanArchitecture contains e.g. PostsViewModel :
final class PostsViewModel: ViewModelType {
struct Input {
let trigger: Driver<Void>
let createPostTrigger: Driver<Void>
let selection: Driver<IndexPath>
}
struct Output {
let fetching: Driver<Bool>
let posts: Driver<[PostItemViewModel]>
let createPost: Driver<Void>
let selectedPost: Driver<Post>
let error: Driver<Error>
}
private let useCase: PostsUseCase
private let navigator: PostsNavigator
init(useCase: PostsUseCase, navigator: PostsNavigator) {
self.useCase = useCase
self.navigator = navigator
}
func transform(input: Input) -> Output {
let activityIndicator = ActivityIndicator()
let errorTracker = ErrorTracker()
let posts = input.trigger.flatMapLatest {
return self.useCase.posts()
.trackActivity(activityIndicator)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
.map { $0.map { PostItemViewModel(with: $0) } }
}
let fetching = activityIndicator.asDriver()
let errors = errorTracker.asDriver()
let selectedPost = input.selection
.withLatestFrom(posts) { (indexPath, posts) -> Post in
return posts[indexPath.row].post
}
.do(onNext: navigator.toPost)
let createPost = input.createPostTrigger
.do(onNext: navigator.toCreatePost)
return Output(fetching: fetching,
posts: posts,
createPost: createPost,
selectedPost: selectedPost,
error: errors)
}
}

Related

In Swift, how can you call a function immediately after object creation

I have some objects, which are structs, that I initialize from JSON dictionaries ([String : Any]) via an init function provided from an extension on the Decodable protocol (see Init an object conforming to Codable with a dictionary/array).
So basically, I have objects that look like this:
struct ObjectModelA: Codable {
var stringVal: String
var intVal: Int
var boolVal: Bool
// Coding keys omitted for brevity
}
struct ObjectModelB: Codable {
var doubleVal: Double
var arrayOfObjectModelAVal: [ObjectModelA]
// Coding keys omitted for brevity
var complicatedComputedVar: String {
// expensive operations using the values in arrayOfObjectModelAVal
}
}
ObjectModelB contains an array of ObjectModelA, and it also has a property which I only really want to set if the arrayOfObjectModelAVal changes.
I can use a didSet on arrayOfObjectModelAVal, but that only catches future changes to the arrayOfObjectModelAVal property. The problem is that I'm using a webservice to retrieve JSON data to create an array of ObjectModelB ([[String : Any]]), and I build it like this:
guard let objectDictArray = responseObject as? [[String : Any]] else { return }
let objects = objectDictArray.compactMap({ try? ObjectModelB(any: $0) })
My objects get created inside the compactMap closure, and init doesn't trigger the didSet.
I also can't "override" the init provided by the init from the Decodable protocol (the one in the closure: try? ObjectModelB(any: $0)) because my object is a struct and this isn't inheritance, it's just an initializer provided by a protocol. Otherwise, I'd "override" the init in my object and then just do super.init followed by some sort of mutating function that updates my complicated property and I'd make my complicated property private(set).
The only other option I can think of is creating that mutating function I just mentioned, and calling it in both the didSet when arrayOfObjectModelAVal changes, and then update my object initialization call with something like this:
guard let objectDictArray = responseObject as? [[String : Any]] else { return }
let objects = objectDictArray
.compactMap({ try? ObjectModelB(any: $0) })
.forEach({ $0.updateProperties() })
But now updateProperties could be called at any time by anyone (which is bad because it's really taxing), and there's no guarantee that it even gets called when creating the array of objects because the dev could forget to do the forEach part. Hence why I want a way to automatically call the updateProperties function right after object initialization.
I figured out a way to accomplish this using a factory method. Like I said in the original question, the initializer I want to use is being provided by a protocol extension on Decodable (see Init an object conforming to Codable with a dictionary/array). I went ahead and added a createFrom static func inside of the Decodable extension like this:
extension Decodable {
init(any: Any) throws {
// https://stackoverflow.com/questions/46327302
}
static func createFrom(any: Any) throws -> Self {
return try Self.init(any: any)
}
}
Now if I define an init on ObjectModelB with the same function signature as the init provided in the Decodable extension, like so:
struct ObjectModelB: Codable {
var doubleVal: Double {
didSet {
computeComplicatedVar()
}
}
var arrayOfObjectModelAVal: [ObjectModelA] {
didSet {
computeComplicatedVar()
}
}
// Coding keys omitted for brevity
private(set) var complicatedVar: String = ""
mutating private func computeComplicatedVar() {
// complicated stuff here
}
init() {
doubleVal = 0.0
arrayOfObjectModelAVal = []
}
init(any: Any) throws {
self.init()
self = try ObjectModelB.createFrom(any: any)
computeComplicatedVar()
}
}
This seems to work. I like it because if I don't add the init that exactly matches the one provided in the Decodable extension, then my object can still use the one provided in the Decodable extension. But if I do provide my own, I just use the createFrom factory method to create an instance of my type using the init from Decodable, and then do whatever else I want after that. This way, I control which objects need special init treatment and which ones don't, but at the point of creating the object, nothing changes. You still use the same init(any:) function.

Reference Types/Subclassing, and Changes to Swift 4 Codable & encoder/decoders

I'm struggling to understand class/reference type behavior and how this relates to changes as I try to upgrade and reduce code using Codable in Swift 4.
I have two classes – a SuperClass with all of the data that will be persistent and that I save to UserDefaults (a place name & string with coordinates), and a SubClass that contains additional, temporary info that I don't need (weather data for the SuperClass coordinates).
In Swift 3 I used to save data like this:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
// subClassArray is of type [SubClass] and contains more data per element.
superClassArray = subClassArray
let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
UserDefaults.standard.set(superClassData, forKey: " superClassData")
}
SuperClass conformed to NSObject & NSCoding
It also included the required init decoder & the encode function.
It all worked fine.
In trying to switch to Swift 4 & codable I've modified SuperClass to conform to Codable.
SuperClass now only has one basic initializer and none of the encoder/decoder stuff from Swift 3. There is no KeyedArchiving happening with this new approach (below). SubClass remains unchanged. Unfortunately I crash on the line where I try? encoder.encode [giving a Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)]. My assumption is that the encoder is getting confused with identical reference types where one is SuperClass and one SubClass (subClassArray[0] === superClassArray[0] is true).
I thought this might work:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
superClassArray = subClassArray
// assumption was that the subclass would only contain parts of the superclass & wouldn't produce an error when being encoded
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(superClassArray){
UserDefaults.standard.set(encoded, forKey: " superClassArray ")
} else {
print("Save didn't work!")
}
}
Then, instead of creating an empty superClassArray, then using:
superClassArray = subClassArray, as shown above, I replace this with the single line:
let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name: $0.name, coordinates: $0.coordinates)}
This works. Again, assumption is because I'm passing in the values inside of the class reference type & haven't made the superClassArray = subClassArray. Also, as expected, subClassArray[0] === superClassArray[0] is false
So why did the "old stuff" in Swift 3 work, even though I used the line superClassArray = subClassArray before the let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
? Am I essentially achieving the same result by creating the array in Swift 4 that was happening with the old Swift 3 encoder/decoder? Is the looping / recreation
Thanks!
Polymorphic persistence appears to be broken by design.
The bug report SR-5331 quotes the response they got on their Radar.
Unlike the existing NSCoding API (NSKeyedArchiver), the new Swift 4 Codable implementations do not write out type information about encoded types into generated archives, for both flexibility and security. As such, at decode time, the API can only use the concrete type your provide in order to decode the values (in your case, the superclass type).
This is by design — if you need the dynamism required to do this, we recommend that you adopt NSSecureCoding and use NSKeyedArchiver/NSKeyedUnarchiver
I am unimpressed, having thought from all the glowing articles that Codable was the answer to some of my prayers. A parallel set of Codable structs that act as object factories is one workaround I'm considering, to preserve type information.
Update I have written a sample using a single struct that manages recreating polymorphic classes. Available on GitHub.
I was not able to get it to work easily with subclassing. However, classes that conform to a base protocol can apply Codable for default encoding. The repo contains both keyed and unkeyed approaches. The simpler is unkeyed, copied below
// Demo of a polymorphic hierarchy of different classes implementing a protocol
// and still being Codable
// This variant uses unkeyed containers so less data is pushed into the encoded form.
import Foundation
protocol BaseBeast {
func move() -> String
func type() -> Int
var name: String { get }
}
class DumbBeast : BaseBeast, Codable {
static let polyType = 0
func type() -> Int { return DumbBeast.polyType }
var name:String
init(name:String) { self.name = name }
func move() -> String { return "\(name) Sits there looking stupid" }
}
class Flyer : BaseBeast, Codable {
static let polyType = 1
func type() -> Int { return Flyer.polyType }
var name:String
let maxAltitude:Int
init(name:String, maxAltitude:Int) {
self.maxAltitude = maxAltitude
self.name = name
}
func move() -> String { return "\(name) Flies up to \(maxAltitude)"}
}
class Walker : BaseBeast, Codable {
static let polyType = 2
func type() -> Int { return Walker.polyType }
var name:String
let numLegs: Int
let hasTail: Bool
init(name:String, legs:Int=4, hasTail:Bool=true) {
self.numLegs = legs
self.hasTail = hasTail
self.name = name
}
func move() -> String {
if numLegs == 0 {
return "\(name) Wriggles on its belly"
}
let maybeWaggle = hasTail ? "wagging its tail" : ""
return "\(name) Runs on \(numLegs) legs \(maybeWaggle)"
}
}
// Uses an explicit index we decode first, to select factory function used to decode polymorphic type
// This is in contrast to the current "traditional" method where decoding is attempted and fails for each type
// This pattern of "leading type code" can be used in more general encoding situations, not just with Codable
//: **WARNING** there is one vulnerable practice here - we rely on the BaseBeast types having a typeCode which
//: is a valid index into the arrays `encoders` and `factories`
struct CodableRef : Codable {
let refTo:BaseBeast //In C++ would use an operator to transparently cast CodableRef to BaseBeast
typealias EncContainer = UnkeyedEncodingContainer
typealias DecContainer = UnkeyedDecodingContainer
typealias BeastEnc = (inout EncContainer, BaseBeast) throws -> ()
typealias BeastDec = (inout DecContainer) throws -> BaseBeast
static var encoders:[BeastEnc] = [
{(e, b) in try e.encode(b as! DumbBeast)},
{(e, b) in try e.encode(b as! Flyer)},
{(e, b) in try e.encode(b as! Walker)}
]
static var factories:[BeastDec] = [
{(d) in try d.decode(DumbBeast.self)},
{(d) in try d.decode(Flyer.self)},
{(d) in try d.decode(Walker.self)}
]
init(refTo:BaseBeast) {
self.refTo = refTo
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let typeCode = try container.decode(Int.self)
self.refTo = try CodableRef.factories[typeCode](&container)
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let typeCode = self.refTo.type()
try container.encode(typeCode)
try CodableRef.encoders[typeCode](&container, refTo)
}
}
struct Zoo : Codable {
var creatures = [CodableRef]()
init(creatures:[BaseBeast]) {
self.creatures = creatures.map {CodableRef(refTo:$0)}
}
func dump() {
creatures.forEach { print($0.refTo.move()) }
}
}
//: ---- Demo of encoding and decoding working ----
let startZoo = Zoo(creatures: [
DumbBeast(name:"Rock"),
Flyer(name:"Kookaburra", maxAltitude:5000),
Walker(name:"Snake", legs:0),
Walker(name:"Doggie", legs:4),
Walker(name:"Geek", legs:2, hasTail:false)
])
startZoo.dump()
print("---------\ntesting JSON\n")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let encData = try encoder.encode(startZoo)
print(String(data:encData, encoding:.utf8)!)
let decodedZoo = try JSONDecoder().decode(Zoo.self, from: encData)
print ("\n------------\nAfter decoding")
decodedZoo.dump()
Update 2020-04 experience
This approach continues to be more flexible and superior to using Codable, at the cost of a bit more programmer time. It is used very heavily in the Touchgram app which provides rich, interactive documents inside iMessage.
There, I need to encode multiple polymorphic hierarchies, including different Sensors and Actions. By storing signatures of decoders, it not only provides with subclassing but also allows me to keep older decoders in the code base so old messages are still compatible.

ReSwiftRecorder Add Action with property

Recently I have used ReSwift API, And I want to add ReSwiftRecorder too!
The sample of ReSwiftRecorder in Github is very simple app
I need to to something more complicated. I have an object which get data from server and I need to It reloads its data when app is not connected to net. Here is my code:
AppState:
struct AppState: StateType {
var menus: Result<[Menu]>?
}
MenuReducer:
func menusReducer(state: Result<[Menu]>?, action: Action) -> Result<[Menu]>? {
switch action {
case let action as SetMenusAction:
return action.menus
default:
return state
}
}
AppReducer:
struct AppReducer: Reducer {
func handleAction(action: Action, state: AppState?) -> AppState {
return AppState(
menus: menusReducer(state: state?.menus, action: action),
)
}
}
MenuActions:
struct SetMenus: Action {
let menus: Result<[Menu]>
}
I know I need to change MenuAction to Something like this:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {}
init(_ standardAction: StandardAction) {}
func toStandardAction() -> StandardAction {
return StandardAction(type: SetMenusAction.type, payload: [:], isTypedAction: true)
}
}
but I got error on init functions
Return from initializer without initializing all stored properties
when I set a initializer code the error disappear but app does not restore saved data! How can I fix it?
You will want to add serialization/deserialization code. The menus property needs to be set. Also, you will want to serialize that property as payload:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {
self.menus = // however you initialize that
}
init(_ standardAction: StandardAction) {
let maybeMenus = standardAction.payload["menus"] as? [Menu]?
self.menus = // create Result from Optional<[Menu]>
}
func toStandardAction() -> StandardAction {
let maybeMenus = self.menus.asOptional // Cannot serialize Result itself
return StandardAction(type: SetMenusAction.type, payload: ["menus" : maybeMenus], isTypedAction: true)
}
}
So problems I see here: JSON serialization depends on Dictionary representation of your payload data, i.e. the properties of your object. Can Result be serialized directly? I guess not, so you need to convert it, probably easiest to nil.
All in all, the payload is the key you missed and now you have to figure out how to use it with the data you have at hand. Also, it makes me a bit suspicious that the Result type itself is part of the AppState. I expected it to be reduced away or handled before dispatching an action, like SettingMenusFailedAction instead of ChangeMenusAction(result:) or similar. Just as a sidenote: actions should be more than typed property setters.

Generic parameter constraint on class crashes the compiler

This is a simplified form of some swift3 code:
class GenericListViewModel<CellViewModel> {
let cells: [CellViewModel]
required init(cells: [CellViewModel]) {
self.cells = cells
}
}
class ViewController<CellViewModel, ListViewModel: GenericListViewModel<CellViewModel>> {
var viewModel: ListViewModel
init(cellViewModels: [CellViewModel]) {
viewModel = ListViewModel(cells: cellViewModels)
}
}
The compiler crashes with the following error:
While emitting IR SIL function #_TFC4Xxxx14ViewControllercfT14cellViewModelsGSax__GS0_xq__ for 'init'
at /.../GenericStuff.swift:22:5
Am is missing something, or is this a Swift compiler bug?
Edit:
I reported this here https://bugs.swift.org/browse/SR-3315 and it looks like it's fixed in current swift master branch.
You're pushing the system too hard with inheritance. Generics based on subclasses of other generics tends to break the compiler's brain, and is usually not really what you meant anyway. (That said: there is never an excuse for the compiler crashing, so you should absolutely open a bugreport.)
Do you really mean to subclass GenericListViewModel and then parameterize ViewController on that precise subclass? This seems very over-complicated and I'm not seeing how you would get any actual value out of it (since you can't rely on any additional methods added to your subclasses, and you already have dynamic dispatch). You're using both subclasses and generics to solve the same problem.
What you likely mean is that there's a CellViewModel and you want GenericListViewModel<CellViewModel> to wrap that, and aren't thinking about subclasses at all.
So, assuming you don't really mean to parameterize this specifically, let inheritance do its job. ListViewModel should be typealias, not a type parameter:
class ViewController<CellViewModel> {
typealias ListViewModel = GenericListViewModel<CellViewModel>
var viewModel: ListViewModel
init(cellViewModels: [CellViewModel]) {
viewModel = ListViewModel(cells: cellViewModels)
}
}
Now it's fine. That said, do you really need the view model to be a reference type? View models often don't need identity themselves (unless you're observing them with KVO). They may wrap a reference type, but as an adapter, a value type is often fine. Assuming this is true for you, then this can and should be simplified to a struct:
struct GenericListViewModel<CellViewModel> {
let cells: [CellViewModel]
}
class ViewController<CellViewModel> {
typealias ListViewModel = GenericListViewModel<CellViewModel>
var viewModel: ListViewModel
init(cellViewModels: [CellViewModel]) {
viewModel = ListViewModel(cells: cellViewModels)
}
}
To your goals of "custom logic like filtering the model, or keeping some other state specific to each controller," I would be very careful of using subclasses for this. It sounds like you're tempted to mix too much functionality into a single type. First, think about how you'd call your code the way you're thinking about it. ListViewModel isn't constrained by the init call, so you can't use type-inference. You'll have to initialize it like:
let vc: ViewController<SomeCellModel, GenericListViewModelSubclass<SomeCellModel>> = ViewController(cells: cells)
That's pretty hideous and is fighting all the things Swift wants to help you with. Since you want to be able to pass in the ListViewModel type, let's just pass it in. This is what protocols are for, not classes.
protocol CellViewModelProviding {
associatedtype CellViewModel
var cells: [CellViewModel] { get }
}
class ViewController<ListViewModel: CellViewModelProviding> {
var viewModel: ListViewModel
init(listViewModel: ListViewModel) {
viewModel = listViewModel
}
}
Now we can create different providers.
// A more standard name for your GenericListViewModel
struct AnyListViewModel<CellViewModel>: CellViewModelProviding {
let cells: [CellViewModel]
}
struct FilteredListViewModel<CellViewModel>: CellViewModelProviding {
var cells: [CellViewModel] {
return unfilteredCells.filter(predicate)
}
var unfilteredCells: [CellViewModel]
var predicate: (CellViewModel) -> Bool
}
Now we can use it with:
let vc = ViewController(listViewModel: AnyListViewModel(cells: [1,2,3]))
let vc2 = ViewController(listViewModel: FilteredListViewModel(unfilteredCells: [1,2,3],
predicate: { $0 % 2 == 0 }))
So that's pretty nice, but we could do better. It's kind of annoying to have to wrap our cells up in an AnyListViewModel in the normal case. We could probably create a factory method to get around this, but yuck. The better answer is to make use of the power of AnyListViewModel to be a type eraser. This is going to get a little more advanced, so if you're happy with the above solution, you can stop, but let's walk through it because it's really powerful and flexible if you need it.
First, we convert AnyListViewModel into a full type eraser that can accept either another view list model, or just an array.
struct AnyListViewModel<CellViewModel>: CellViewModelProviding {
private let _cells: () -> [CellViewModel]
var cells: [CellViewModel] { return _cells() }
init(cells: [CellViewModel]) {
_cells = { cells }
}
init<ListViewModel: CellViewModelProviding>(_ listViewModel: ListViewModel)
where ListViewModel.CellViewModel == CellViewModel {
_cells = { listViewModel.cells }
}
}
Now ViewController doesn't have to care what kind of ListViewModel is passed. It can turn anything into an AnyListViewModel and work with that.
class ViewController<CellViewModel> {
var viewModel: AnyListViewModel<CellViewModel>
init<ListViewModel: CellViewModelProviding>(listViewModel: ListViewModel)
where ListViewModel.CellViewModel == CellViewModel {
viewModel = AnyListViewModel(listViewModel)
}
init(cells: [CellViewModel]) {
viewModel = AnyListViewModel(cells: cells)
}
}
OK, that's cool, but it's not a huge improvement. Well, let's rebuild FilteredListViewModel and see what that gets us.
struct FilteredListViewModel<CellViewModel>: CellViewModelProviding {
var cells: [CellViewModel] {
return listViewModel.cells.filter(predicate)
}
private var listViewModel: AnyListViewModel<CellViewModel>
var predicate: (CellViewModel) -> Bool
// We can lift any other listViewModel
init<ListViewModel: CellViewModelProviding>(filtering listViewModel: ListViewModel,
withPredicate predicate: #escaping (CellViewModel) -> Bool)
where ListViewModel.CellViewModel == CellViewModel {
self.listViewModel = AnyListViewModel(listViewModel)
self.predicate = predicate
}
// Or, just for convenience, we can handle the simple [cell] case
init(filtering cells: [CellViewModel], withPredicate predicate: #escaping (CellViewModel) -> Bool) {
self.init(filtering: AnyListViewModel(cells: cells), withPredicate: predicate)
}
}
This is where things get powerful. We've said FilteredListViewModel can take some cells and filter them, sure. But it can also filter any other view list model.
let someList = AnyListViewModel(cells: [1,2,3])
let evenList = FilteredListViewModel(filtering: someList, withPredicate: { $0 % 2 == 0 })
So now you can chain things together. You could glue together filtering with sorting or something that modified the cells or whatever. You don't need one super-specialized subclass that does everything you need. You can click together simpler pieces to build complex solutions.

iterate over struct attributes in Swift

I am using struct in swift.
class Constants {
struct const {
static let signupFirstName = "signupFirstName"
}
}
I want to iterate the struct. For iterating I am using :
let mirrored_object = Mirror(reflecting: Constants.const())
for (index, attr) in mirrored_object.children.enumerate() {
if let property_name = attr.label as String! {
print("Attr \(index): \(property_name) = \(attr.value)")
}
}
But it does not enter into the code because of static value. Is there any way to iterate this struct?
Since static members are technically part of the type and not the instance, you would need to approach it this way to reflect on the type itself:
let mirrored_object = Mirror(reflecting: Constants.const.self)
However, Swift's automatic reflection for types themselves doesn't appear to be implemented at this time, so even the above line won't work.
That leaves one last option, which is defining your own custom mirror for reflecting on an instance of your type. That could look something like this:
class Constants {
struct const : CustomReflectable {
static let signupFirstName = "signupFirstName"
func customMirror() -> Mirror {
return Mirror(self, children: ["signupFirstName" : const.signupFirstName])
}
}
}
If you modify your code with a CustomReflectable implementation similar to the above, your loop to iterate through the struct members will now work.
Swift reflection will eventually get better out of the box, you might want to try a different approach until then.
You can do it directly by accessing the variable at class level
if let property_value = const.signupFirstName as String! {
print("hello \(property_value)")
}
Be sure to access it from the class it self const not an instance const(). Because the variable is static you can not access it from instance, you have to directly use the class