When starting a new app, after deciding which based design pattern you are going to use for the views, you can create some Xcode templates to help standardize how the files are written and speed up development.
For example, let’s say we decide to use MVVM for our SwiftUI views. We also decide we will have 3 files for each view:
- MyView_Mocks.swift
- MyView.swift
- MyViewModel.swift
Create the Template
We can create a basic Xcode template that creates those 3 files with some basic code inside by following these steps:
- Navigate to
~/Library/Developer/Xcode/Templates
. (Create the Templates folder if necessary). - Create a new
File Templates
folder inside. - Create a new folder called
SwiftUIView-ViewModel.xctemplate
insideFile Templates
. - Create these 4 files inside the new folder (code below):
___FILEBASENAME____Mocks.swift
___FILEBASENAME___.swift
___FILEBASENAME___Model.swift
TemplateInfo.plist
After this, reset Xcode and you will see this option when adding a new file:
The code for the files
You can customize these files to suit your needs:
View
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//___FILEHEADER___ | |
import SwiftUI | |
struct ___VARIABLE_ViewName___: View { | |
@StateObject private var viewModel: ViewModel | |
init(dependencies: ViewModel.Dependencies) { | |
_viewModel = StateObject( | |
wrappedValue: ViewModel( | |
dependencies: dependencies | |
) | |
) | |
} | |
var body: some View { | |
Text("___VARIABLE_ViewName___") // TODO: Replace the body | |
} | |
} | |
private extension ___VARIABLE_ViewName___ { | |
enum ViewConstants { | |
// TODO: Add constants here | |
} | |
} | |
struct ___VARIABLE_ViewName____Previews: PreviewProvider { | |
static var previews: some View { | |
NavigationView { | |
___VARIABLE_ViewName___(dependencies: .mock) | |
} | |
} | |
} |
ViewModel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//___FILEHEADER___ | |
import Foundation | |
extension ___VARIABLE_ViewName___ { | |
final class ViewModel: ObservableObject { | |
private let dependencies: Dependencies | |
init(dependencies: Dependencies = .default) { | |
self.dependencies = dependencies | |
} | |
} | |
} | |
extension ___VARIABLE_ViewName___.ViewModel { | |
struct Dependencies { | |
// TODO: Add dependencies here | |
var doSomething: () async throws -> Void | |
static var `default`: Self { | |
.init { | |
// TODO: Provide the default dependencies here | |
return | |
} | |
} | |
} | |
} | |
extension ___VARIABLE_ViewName___.ViewModel { | |
// TODO: Replace me | |
@MainActor | |
func doSomething() async { | |
do { | |
try await dependencies.doSomething() | |
} catch { | |
// Error handling | |
} | |
} | |
} | |
private extension ___VARIABLE_ViewName___.ViewModel { | |
} |
Mocks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//___FILEHEADER___ | |
import Foundation | |
extension ___VARIABLE_ViewName___.ViewModel.Dependencies { | |
static var mock: Self { | |
.init { | |
// TODO: Replace me | |
return | |
} | |
} | |
} |
TemplateInfo.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>SupportsSwiftPackage</key> | |
<true/> | |
<key>Kind</key> | |
<string>Xcode.IDEFoundation.TextSubstitutionFileTemplateKind</string> | |
<key>Description</key> | |
<string>A SwiftUI View with it's ViewModel and the Mocks</string> | |
<key>Summary</key> | |
<string>SwiftUI View + ViewModel + Mocks</string> | |
<key>SortOrder</key> | |
<string>1</string> | |
<key>AllowedTypes</key> | |
<array> | |
<string>public.swift-source</string> | |
</array> | |
<key>Platforms</key> | |
<array/> | |
<key>MainTemplateFile</key> | |
<string>___FILEBASENAME___.swift</string> | |
<key>Image</key> | |
<dict> | |
<key>FileTypeIcon</key> | |
<string>swift</string> | |
</dict> | |
<key>Options</key> | |
<array> | |
<dict> | |
<key>Description</key> | |
<string>The name of the view to create.</string> | |
<key>Identifier</key> | |
<string>ViewName</string> | |
<key>Name</key> | |
<string>New View Name:</string> | |
<key>NotPersisted</key> | |
<true/> | |
<key>Required</key> | |
<true/> | |
<key>Type</key> | |
<string>text</string> | |
</dict> | |
<dict> | |
<key>Default</key> | |
<string>___VARIABLE_ViewName:identifier___</string> | |
<key>Identifier</key> | |
<string>productName</string> | |
<key>Type</key> | |
<string>static</string> | |
</dict> | |
</array> | |
</dict> | |
</plist> |