ios-uikit-architecture
Use when building iOS apps with UIKit, implementing MVVM/MVC/Coordinator patterns, or integrating UIKit with SwiftUI.
How do I install this agent skill?
npx skills add https://github.com/thebushidocollective/han --skill ios-uikit-architectureIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides architectural templates and best practices for building iOS applications with UIKit. It includes examples of MVVM, Coordinator patterns, and dependency injection. No malicious patterns or security risks were identified.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- Runlayerpass
1 file scanned · No issues
- ZeroLeakspass
Score: 93/100 · 2 sections analyzed
What does this agent skill do?
iOS - UIKit Architecture
Architectural patterns and best practices for UIKit-based iOS applications.
Key Concepts
MVVM Architecture
The Model-View-ViewModel pattern separates concerns:
- Model: Data and business logic
- View: UIViewController and UIView subclasses
- ViewModel: Presentation logic, transforms model data for display
// Model
struct User {
let id: String
let firstName: String
let lastName: String
let email: String
}
// ViewModel
class UserProfileViewModel {
private let user: User
var displayName: String {
"\(user.firstName) \(user.lastName)"
}
var emailDisplay: String {
user.email.lowercased()
}
init(user: User) {
self.user = user
}
}
// View
class UserProfileViewController: UIViewController {
private let viewModel: UserProfileViewModel
init(viewModel: UserProfileViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = viewModel.displayName
emailLabel.text = viewModel.emailDisplay
}
}
Coordinator Pattern
Coordinators handle navigation flow, removing navigation logic from view controllers:
protocol Coordinator: AnyObject {
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController { get }
func start()
}
class AppCoordinator: Coordinator {
var childCoordinators: [Coordinator] = []
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = HomeViewController()
vc.coordinator = self
navigationController.pushViewController(vc, animated: false)
}
func showDetail(for item: Item) {
let detailCoordinator = DetailCoordinator(
navigationController: navigationController,
item: item
)
childCoordinators.append(detailCoordinator)
detailCoordinator.start()
}
}
Dependency Injection
Inject dependencies through initializers for testability:
protocol UserServiceProtocol {
func fetchUser(id: String) async throws -> User
}
class UserViewController: UIViewController {
private let userService: UserServiceProtocol
private let userId: String
init(userService: UserServiceProtocol, userId: String) {
self.userService = userService
self.userId = userId
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) not supported")
}
}
Best Practices
Programmatic UI with Auto Layout
class ProfileView: UIView {
private let avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private let nameLabel: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .headline)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
private func setupViews() {
addSubview(avatarImageView)
addSubview(nameLabel)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
avatarImageView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
avatarImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
avatarImageView.widthAnchor.constraint(equalToConstant: 80),
avatarImageView.heightAnchor.constraint(equalToConstant: 80),
nameLabel.topAnchor.constraint(equalTo: avatarImageView.bottomAnchor, constant: 12),
nameLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
])
}
}
Modern Collection Views with Diffable Data Source
class ItemListViewController: UIViewController {
enum Section { case main }
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
private var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
configureDataSource()
}
private func configureCollectionView() {
let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
let layout = UICollectionViewCompositionalLayout.list(using: config)
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(collectionView)
}
private func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { cell, indexPath, item in
var content = cell.defaultContentConfiguration()
content.text = item.title
content.secondaryText = item.subtitle
cell.contentConfiguration = content
}
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) {
collectionView, indexPath, item in
collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
}
}
func updateItems(_ items: [Item]) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.main])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
UIKit and SwiftUI Integration
Hosting SwiftUI in UIKit:
class SettingsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swiftUIView = SettingsView()
let hostingController = UIHostingController(rootView: swiftUIView)
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.view.frame = view.bounds
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostingController.didMove(toParent: self)
}
}
Wrapping UIKit in SwiftUI:
struct MapViewRepresentable: UIViewRepresentable {
@Binding var region: MKCoordinateRegion
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
mapView.setRegion(region, animated: true)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapViewRepresentable
init(_ parent: MapViewRepresentable) {
self.parent = parent
}
}
}
Common Patterns
View Controller Lifecycle Management
class DataViewController: UIViewController {
private var loadTask: Task<Void, Never>?
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadData()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
loadTask?.cancel()
}
private func loadData() {
loadTask = Task {
do {
let data = try await fetchData()
guard !Task.isCancelled else { return }
updateUI(with: data)
} catch {
showError(error)
}
}
}
}
Memory Management with Closures
class NetworkViewController: UIViewController {
private let networkService: NetworkService
func fetchData() {
// Use [weak self] to prevent retain cycles
networkService.fetch { [weak self] result in
guard let self else { return }
switch result {
case .success(let data):
self.handleData(data)
case .failure(let error):
self.showError(error)
}
}
}
}
Anti-Patterns
Massive View Controllers
Bad: Putting everything in one view controller.
Good: Extract into separate types:
- ViewModels for presentation logic
- Coordinators for navigation
- Custom views for UI components
- Services for network/data operations
Storyboard Segue Spaghetti
Bad: Complex storyboard with many segues.
Good: Use coordinators with programmatic navigation.
Force Casting Cells
Bad:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! CustomCell
Good:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? CustomCell else {
fatalError("Unable to dequeue CustomCell")
}
Related Skills
- ios-swiftui-patterns: Modern declarative UI
- ios-swift-concurrency: Async data loading
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/thebushidocollective/han/ios-uikit-architecture">View ios-uikit-architecture on skillZs</a>