Closed Jeehut closed 3 years ago
I have created an Xcode template for this kind workflow.
It’s pretty easy!
On Sat, 21 Nov 2020 at 13:08, Cihat Gündüz notifications@github.com wrote:
I'm just getting started with TCA and I already have the problem that each time I create a new composable, there's lots of boilerplate code to write, files to create etc. – I've actually created a swift-sh https://github.com/mxcl/swift-sh script for me to simplify at least the creation of new composables, see here: generate.swift
!/usr/local/bin/swift-sh
import Foundation import Files // @JohnSundell ~> 4.2 import ShellOut // @JohnSundell ~> 2.3 import HandySwift // @Flinesoft == 246400b6ef9768a7bc9f4e58f9102664e46d4aea
// MARK: - Input Handling
let usageInstructons: String = """
Run like this: ./generate.swift
Replace with one of: composable Replace with the name to use for your generated file(s). For example: ./generate.swift composable Login """
guard CommandLine.arguments.count == 3 else {
print("ERROR: Wrong number of arguments. Expected 2, got (CommandLine.arguments.count - 1).")
print(usageInstructons)
exit(EXIT_FAILURE)
}
enum Kind: String, CaseIterable {
case composable
}
guard let kind = Kind(rawValue: CommandLine.arguments[1]) else {
print("ERROR: Unknown kind '(CommandLine.arguments[1])'. Use one of: (Kind.allCases)")
print(usageInstructons)
exit(EXIT_FAILURE)
}
let name = CommandLine.arguments[2]
// MARK: - Defining File Contents
func viewFileContents(name: String) -> String {
""" import ComposableArchitecture import SwiftUI
struct (name)View: View { let store: Store<(name)State, (name)Action>
var body: some View { WithViewStore(store) { viewStore in Button("Example") { viewStore.send(.exampleButtonClicked) } } .padding() }
}
struct (name)View_Previews: PreviewProvider { static var previews: some View { (name)View( store: Store( initialState: (name)State(), reducer: (name.firstLowercased)Reducer, environment: (name)Environment() ) ) } }
"""
}
func stateFileContents(name: String) -> String {
""" import Foundation
struct (name)State: Equatable {
warning("TODO: not yet implemented")
}
"""
}
func actionFileContents(name: String) -> String {
""" import Foundation
enum (name)Action {
warning("TODO: not yet implemented")
case exampleButtonClicked
}
"""
}
func environmentFileContents(name: String) -> String {
""" import Foundation
struct (name)Environment {
warning("TODO: not yet implemented")
}
"""
}
func reducerFileContents(name: String) -> String {
""" import ComposableArchitecture import Foundation
let (name.firstLowercased)Reducer = Reducer<(name)State, (name)Action, (name)Environment>() { state, action, environment in
warning("TODO: not yet implemented")
switch action { case .exampleButtonClicked: print("example button was clicked") return .none }
}
"""
}
// MARK: - Generating Code Files
switch kind { case .composable:
let sourcesFolder = try Folder(path: "Shared/Sources/Composables")
guard !sourcesFolder.containsSubfolder(named: name) else {
print("ERROR: There's already a folder named '\(name)' in Shared/Sources, please delete it first and retry.") exit(EXIT_FAILURE)
}
let folder = try sourcesFolder.createSubfolder(at: name)
let viewFile = try folder.createFile(named: "(name)View.swift")
try viewFile.write(viewFileContents(name: name))
let stateFile = try folder.createFile(named: "(name)State.swift")
try stateFile.write(stateFileContents(name: name))
let actionFile = try folder.createFile(named: "(name)Action.swift")
try actionFile.write(actionFileContents(name: name))
let environmentFile = try folder.createFile(named: "(name)Environment.swift")
try environmentFile.write(environmentFileContents(name: name))
let reducerFile = try folder.createFile(named: "(name)Reducer.swift")
try reducerFile.write(reducerFileContents(name: name))
print("Successfully generated files. Drag & drop folder to Xcode to finish.")
try shellOut(to: "open", arguments: [folder.path, "--reveal"])
}
With the above, I can run ./generate.swift composable Dashboard and it will create the following files: DashboardView.swift
import ComposableArchitecture import SwiftUI
struct DashboardView: View {
let store: Store<DashboardState, DashboardAction>
var body: some View {
WithViewStore(store) { viewStore in Button("Example") { viewStore.send(.exampleButtonClicked) } } .padding()
}
}
struct DashboardView_Previews: PreviewProvider {
static var previews: some View {
DashboardView( store: Store( initialState: DashboardState(), reducer: dashboardReducer, environment: DashboardEnvironment() ) )
}
}
DashboardState.swift
import Foundation
struct DashboardState: Equatable {
warning("TODO: not yet implemented")
}
DashboardEnvironment.swift
import Foundation
struct DashboardEnvironment {
warning("TODO: not yet implemented")
}
DashboardReducer.swift
import ComposableArchitecture import Foundation
let dashboardReducer = Reducer<DashboardState, DashboardAction, DashboardEnvironment>() { state, action, environment in
warning("TODO: not yet implemented")
switch action {
case .exampleButtonClicked: print("example button was clicked") return .none
}
}
DashboardAction.swift
import Foundation
enum DashboardAction {
warning("TODO: not yet implemented")
case exampleButtonClicked
}
While this works for me for now, I think 1. it should be part of some official TCA command line tool and 2. there will probably be some more common unnecessary manual work places where a tool could help out pretty nicely.
So, what do you think about adding an official command line tool to TCA?
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pointfreeco/swift-composable-architecture/issues/326, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANVHGKF7I5X5UV3NKMLDE3SQ6NS3ANCNFSM4T5W7U5A .
-- Γιώργος
@gkaimakas Thanks for pointing out Xcode templates, but I have a few questions related to them:
I believe they are supported by Apple since it’s the same think Xcode uses to generate files e.g when you create a new UIKit class.
I haven’t found any collection of templates online but there are a lot of tutorials on how to create one yourself.
Rolling out a template to a team is super easy, barely an inconvenience I’d say. What I have done is added them in git and each new member that wants to use them can run a simple script to “install” them (or a build phase to do automatically)
Flexibility is a subjective. The template allows me to generate the scaffolding for a TCA feature fast, like different files for the environment, reducer, state, action and the view / view controller etc. The rest is up to me to do.
I will try and share an example with you later today.
On Sat, 21 Nov 2020 at 13:20, Cihat Gündüz notifications@github.com wrote:
@gkaimakas https://github.com/gkaimakas Thanks for pointing out Xcode templates, but I have a few questions related to them:
- Are "Xcode templates" even an official feature Apples supports? I can't find any official documentation from Apple ...
- Is there anywhere a collection of Xcode templates where we could add TCA-specific ones for the community?
- How easy is it to roll out the "Xcode templates" approach to a team? How can everyone set them up easily?
- Do Xcode templates flexible enough for us to cover most common actions we need to do in TCA?
— You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/pointfreeco/swift-composable-architecture/issues/326#issuecomment-731565211, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANVHGI664TGKPYVGST6UVDSQ6PAJANCNFSM4T5W7U5A .
-- Γιώργος
@gkaimakas That'd be great, thank you!
Hi @Jeehut, thanks for bringing this topic up!
We are reluctant to have any official support for this in TCA primarily because we do not want to recommend a particular style for people to structure their features. For example, we personally do not separate state, action, environment and reducers into separate files. More often than not we even have the view in the same file too. We prefer to have everything in one spot so that we do not have to jump around to a bunch of files.
On the other hand we know others may like to put all of those pieces into separate files, and that's great if they prefer to have everything separated into shorter files.
I think your tool could be really great for the community, and if you open sourced it it could be added to the directory of TCA related projects: https://github.com/antranapp/awesome-tca. We are also in the planning stages of building a "TCA community" GitHub organization where such projects could also be hosted.
I'm going to close this out for now. Thanks for the discussion!
I'm just getting started with TCA and I already have the problem that each time I create a new composable, there's lots of boilerplate code to write, files to create etc. – I've actually created a swift-sh script for me to simplify at least the creation of new composables, see here:
generate.swift
```swift #!/usr/local/bin/swift-sh import Foundation import Files // @JohnSundell ~> 4.2 import ShellOut // @JohnSundell ~> 2.3 import HandySwift // @Flinesoft == 246400b6ef9768a7bc9f4e58f9102664e46d4aea // MARK: - Input Handling let usageInstructons: String = """ Run like this: ./generate.swiftWith the above, I can run
./generate.swift composable Dashboard
and it will create the following files:DashboardView.swift
```swift import ComposableArchitecture import SwiftUI struct DashboardView: View { let store: StoreDashboardState.swift
```swift import Foundation struct DashboardState: Equatable { #warning("TODO: not yet implemented") } ```DashboardEnvironment.swift
```swift import Foundation struct DashboardEnvironment { #warning("TODO: not yet implemented") } ```DashboardReducer.swift
```swift import ComposableArchitecture import Foundation let dashboardReducer = ReducerDashboardAction.swift
```swift import Foundation enum DashboardAction { #warning("TODO: not yet implemented") case exampleButtonClicked } ```While this works for me for now, I think 1. it should be part of some official TCA command line tool and 2. there will probably be some more common unnecessary manual work places where a tool could help out pretty nicely.
So, what do you think about adding an official command line tool to TCA?