Reusable
A Swift mixin for reusing views easily and in a type-safe way (UITableViewCells, UICollectionViewCells, custom UIViews, ViewControllers, Storyboards…)
Top Related Projects
🌲 Evergreen React UI Framework by Segment
Bootstrap components built with React
Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
An enterprise-class UI design language and React UI library
A utility-first CSS framework for rapid UI development.
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
Quick Overview
Reusable is a Swift library that simplifies the process of creating reusable UITableView and UICollectionView cells. It provides a protocol-oriented approach to register, dequeue, and configure cells, making it easier to work with table and collection views in iOS applications.
Pros
- Reduces boilerplate code for cell registration and dequeuing
- Improves type safety when working with cells
- Supports both UITableView and UICollectionView
- Easy to integrate into existing projects
Cons
- Limited to UIKit-based applications (not compatible with SwiftUI)
- May require a learning curve for developers unfamiliar with protocol-oriented programming
- Adds a dependency to the project, which some developers might prefer to avoid
Code Examples
- Defining a reusable cell:
class MyCell: UITableViewCell, Reusable {
@IBOutlet private weak var titleLabel: UILabel!
func configure(with title: String) {
titleLabel.text = title
}
}
- Registering and dequeuing a cell:
// Register the cell
tableView.register(cellType: MyCell.self)
// Dequeue the cell
let cell: MyCell = tableView.dequeueReusableCell(for: indexPath)
- Using with a custom XIB:
class CustomCell: UICollectionViewCell, NibReusable {}
// Register the cell
collectionView.register(cellType: CustomCell.self)
// Dequeue the cell
let cell: CustomCell = collectionView.dequeueReusableCell(for: indexPath)
Getting Started
-
Add Reusable to your project using Swift Package Manager:
dependencies: [ .package(url: "https://github.com/AliSoftware/Reusable.git", from: "4.1.2") ]
-
Import Reusable in your Swift file:
import Reusable
-
Make your cell conform to the
Reusable
orNibReusable
protocol:class MyCell: UITableViewCell, Reusable { // Cell implementation }
-
Use the provided methods to register and dequeue cells:
tableView.register(cellType: MyCell.self) let cell: MyCell = tableView.dequeueReusableCell(for: indexPath)
Competitor Comparisons
🌲 Evergreen React UI Framework by Segment
Pros of Evergreen
- Comprehensive UI component library with a wide range of pre-built elements
- Consistent design system and theming capabilities
- Extensive documentation and examples for easy implementation
Cons of Evergreen
- Larger bundle size due to the extensive component library
- Less flexibility for custom styling compared to Reusable's approach
- Steeper learning curve for developers new to the library
Code Comparison
Evergreen component usage:
import { Button, Pane } from 'evergreen-ui'
<Pane>
<Button appearance="primary">Click me</Button>
</Pane>
Reusable storyboard setup:
class MyViewController: UIViewController, StoryboardBased {}
let vc = MyViewController.instantiate()
Summary
Evergreen is a comprehensive React UI component library, offering a wide range of pre-built elements and a consistent design system. It's ideal for projects requiring a complete UI toolkit. However, it may have a larger bundle size and less flexibility for custom styling.
Reusable, on the other hand, is a Swift library focused on making UIStoryboard and UIViewController instantiation type-safe and convenient. It's lightweight and provides more flexibility for custom UI development in iOS applications.
The choice between these libraries depends on the specific needs of your project, the platform you're developing for, and the level of customization required.
Bootstrap components built with React
Pros of react-bootstrap
- Extensive collection of pre-built React components
- Large community support and regular updates
- Seamless integration with React applications
Cons of react-bootstrap
- Larger bundle size due to comprehensive component library
- Less flexibility in customizing individual components
- Steeper learning curve for developers new to Bootstrap
Code Comparison
react-bootstrap:
import { Button } from 'react-bootstrap';
function MyComponent() {
return <Button variant="primary">Click me</Button>;
}
Reusable:
class MyViewController: UIViewController, Reusable {
static let reuseIdentifier = "MyViewController"
}
While react-bootstrap focuses on providing React components for Bootstrap, Reusable is a Swift library for making UIViewController instances reusable. The code examples highlight this fundamental difference in purpose and implementation.
react-bootstrap offers a more comprehensive UI toolkit for React developers, while Reusable provides a specific utility for iOS development. The choice between them depends on the project's platform and requirements.
Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
Pros of Material-UI
- Comprehensive UI component library for React applications
- Extensive documentation and community support
- Customizable theming system for consistent design
Cons of Material-UI
- Larger bundle size due to extensive component library
- Steeper learning curve for customization and advanced usage
- Opinionated design system may not fit all project requirements
Code Comparison
Material-UI:
import { Button, TextField } from '@mui/material';
function MyForm() {
return (
<form>
<TextField label="Name" variant="outlined" />
<Button variant="contained" color="primary">Submit</Button>
</form>
);
}
Reusable:
class MyViewController: UIViewController, Reusable {
static let reuseIdentifier = "MyViewController"
override func viewDidLoad() {
super.viewDidLoad()
// Setup UI
}
}
Key Differences
- Material-UI is a React-based UI library, while Reusable is a Swift framework for iOS development
- Material-UI provides ready-to-use UI components, whereas Reusable focuses on view controller reusability
- Material-UI offers a complete design system, while Reusable is more focused on improving code organization and reusability in iOS projects
An enterprise-class UI design language and React UI library
Pros of ant-design
- Comprehensive UI component library for React applications
- Extensive documentation and examples for easy implementation
- Large community support and regular updates
Cons of ant-design
- Larger package size due to its extensive component library
- Steeper learning curve for developers new to the ecosystem
- Less flexibility for custom styling compared to Reusable
Code Comparison
Ant Design component usage:
import { Button } from 'antd';
const MyComponent = () => (
<Button type="primary">Click me</Button>
);
Reusable component usage:
class MyViewController: UIViewController, Reusable {
static let reuseIdentifier = "MyViewController"
}
let vc = MyViewController.instantiate()
While both libraries aim to improve development efficiency, they serve different purposes. Ant Design provides a complete UI component library for React applications, offering a wide range of pre-built components. Reusable, on the other hand, focuses on simplifying the creation and management of reusable views in iOS applications using Swift.
Ant Design is better suited for large-scale React projects requiring a consistent design system, while Reusable is ideal for iOS developers looking to streamline their view management and improve code reusability in Swift projects.
A utility-first CSS framework for rapid UI development.
Pros of Tailwind CSS
- Highly customizable utility-first CSS framework
- Large community and ecosystem with extensive documentation
- Responsive design out of the box
Cons of Tailwind CSS
- Steeper learning curve for developers new to utility-first CSS
- Can lead to verbose HTML markup
- Requires additional build step in projects
Code Comparison
Tailwind CSS:
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Button
</button>
Reusable:
class MyViewController: UIViewController, Reusable {
static let reuseIdentifier = "MyViewController"
}
Summary
Tailwind CSS is a utility-first CSS framework for rapidly building custom user interfaces, while Reusable is a Swift library for making UIView and NSView reusable components. Tailwind CSS offers a comprehensive set of pre-built utility classes for styling, whereas Reusable focuses on simplifying the process of creating reusable views in iOS and macOS development.
Tailwind CSS is more suited for web development projects, providing a flexible approach to styling with its utility classes. Reusable, on the other hand, is specifically designed for Swift developers working on Apple platforms, offering a streamlined way to create and manage reusable UI components.
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
Pros of styled-components
- Allows for CSS-in-JS, enabling dynamic styling based on props
- Provides automatic vendor prefixing and unique class names
- Supports server-side rendering out of the box
Cons of styled-components
- Steeper learning curve for developers unfamiliar with CSS-in-JS
- Potentially larger bundle size due to runtime styling
- May lead to mixing of styling and logic concerns in components
Code Comparison
styled-components:
const Button = styled.button`
background-color: ${props => props.primary ? 'blue' : 'white'};
color: ${props => props.primary ? 'white' : 'blue'};
padding: 10px 20px;
border: 2px solid blue;
`;
Reusable:
class MyViewController: UIViewController, Reusable {
static let reuseIdentifier = "MyViewController"
static func instantiate() -> Self {
// Custom instantiation logic
}
}
Summary
styled-components is a popular CSS-in-JS library for React applications, offering dynamic styling and automatic optimizations. Reusable, on the other hand, is a Swift library focused on making UIKit components more reusable through protocols and extensions. While styled-components provides powerful styling capabilities for web development, Reusable aims to improve code organization and reusability in iOS app development. The choice between these libraries depends on the specific platform and development needs of your project.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Reusable
A Swift mixin to use UITableViewCells
, UICollectionViewCells
and UIViewControllers
in a type-safe way, without the need to manipulate their String
-typed reuseIdentifiers
. This library also supports arbitrary UIView
to be loaded via a XIB using a simple call to loadFromNib()
Installation
Requirements: which Reusable version to use for each Swift Version?
Swift Version | Reusable Version |
---|---|
2.2 & 2.3 | 2.5.1 |
3.0 (â ) | 3.0.0 + |
4.0 | 4.0.2 + |
5.0 | 4.1.0 + |
(â ) The Reusable 3.0 code also compiles with Swift 4, you'll need 4.0.2+ only if you're using Carthage for integration
Reusable can be integrated to your Xcode projects using one of the following options:
Installation instructions for Swift Package Manager (SPM)
Swift Package Manager is Apple's decentralized dependency manager to integrate libraries to your Swift projects. It is now fully integrated with Xcode 11
To integrate Reusable into your project using SPM, specify it in your Package.swift
file:
let package = Package(
â¦
dependencies: [
.package(url: "https://github.com/AliSoftware/Reusable.git", from: "4.1.0"),
],
targets: [
.target(name: "YourTarget", dependencies: ["Reusable", â¦])
â¦
]
)
Installation instructions for Carthage
Carthage is a decentralized dependency manager to add pre-built frameworks to your Cocoa application.
To integrate Reusable into your Xcode project using Carthage, specify it in your Cartfile
:
github "AliSoftware/Reusable"
Then run carthage update --use-xcframeworks
Installation instructions for CocoaPods
CocoaPods is a dependency manager to automate integration of frameworks to your Swift and Objective-C Cocoa projects.
To integrate Reusable into your Xcode project using Cocoapods, specify it in your Podfile
:
pod 'Reusable'
Introduction
This library aims to make it super-easy to create, dequeue and instantiate reusable views anywhere this pattern is used: from the obvious UITableViewCell
and UICollectionViewCell
to custom UIViews
, even supporting UIViewControllers
from Storyboards.
All of that simply by marking your classes as conforming to a protocol, without having to add any code, and creating a type-safe API with no more String-based API.
// Example of what Reusable allows you to do
final class MyCustomCell: UITableViewCell, Reusable { /* And that's it! */ }
tableView.register(cellType: MyCustomCell.self)
let cell: MyCustomCell = tableView.dequeueReusableCell(for: indexPath)
This concept, called a Mixin (a protocol with default implementation for all its methods), is explained here in my blog post in details.
Table of Contents
- Type-safe cells
- Type-safe XIB-based reusable views
- Type-safe ViewControllers from Storyboards
- Additional tips
- Example project
- Talks and Articles about Reusable
- License
Type-safe UITableViewCell
/ UICollectionViewCell
âï¸ Examples and explanations below use
UITableView
andUITableViewCell
, but the exact same examples and explanations apply forUICollectionView
andUICollectionViewCell
.
1. Declare your cells to conform to Reusable
or NibReusable
- Use the
Reusable
protocol if they don't depend on a NIB (this will useregisterClass(â¦)
to register the cell) - Use the
NibReusable
typealias (=Reusable & NibLoadable
) if they use aXIB
file for their content (this will useregisterNib(â¦)
to register the cell)
final class CustomCell: UITableViewCell, Reusable { /* And that's it! */ }
âï¸ Notes
- For cells embedded in a Storyboard's tableView, either one of those two protocols will work (as you won't need to register the cell manually anyway, since registration is handled by the storyboard automatically)
- If you create a XIB-based cell, don't forget to set its Reuse Identifier field in Interface Builder to the same string as the name of the cell class itself.
- ð¡
NibReusable
is a typealias, so you could still use two protocols conformanceReusable, NibLoadable
instead ofNibReusable
.
ð Example for a Code-based custom tableView cell
final class CodeBasedCustomCell: UITableViewCell, Reusable {
// By default this cell will have a reuseIdentifier of "CodeBasedCustomCell"
// unless you provide an alternative implementation of `static var reuseIdentifier`
// No need to add anything to conform to Reusable. You can just keep your normal cell code
@IBOutlet private weak var label: UILabel!
func fillWithText(text: String?) { label.text = text }
}
ð Example for a Nib-based custom tableView cell
final class NibBasedCustomCell: UITableViewCell, NibReusable {
// or
// final class NibBasedCustomCell: UITableViewCell, Reusable, NibLoadable {
// Here we provide a nib for this cell class (which, if we don't override the protocol's
// default implementation of `static var nib: UINib`, will use a XIB of the same name as the class)
// No need to add anything to conform to Reusable. You can just keep your normal cell code
@IBOutlet private weak var pictureView: UIImageView!
func fillWithImage(image: UIImage?) { pictureView.image = image }
}
ð Example for a Code-based custom collectionView cell
// A UICollectionViewCell which doesn't need a XIB to register
// Either because it's all-code, or because it's registered via Storyboard
final class CodeBasedCollectionViewCell: UICollectionViewCell, Reusable {
// The rest of the cell code goes here
}
ð Example for a Nib-based custom collectionView cell
// A UICollectionViewCell using a XIB to define it's UI
// And that will need to register using that XIB
final class NibBasedCollectionViewCell: UICollectionViewCell, NibReusable {
// or
// final class NibBasedCollectionViewCell: UICollectionViewCell, Reusable, NibLoadable {
// The rest of the cell code goes here
}
2. Register your cells
Unless you've prototyped your cell in a Storyboard, you'll have to register the cell class or Nib by code.
To do this, instead of calling registerClass(â¦)
or registerNib(â¦)
using a String-based reuseIdentifier
, just call:
tableView.register(cellType: theCellClass.self)
ð Example of `UITableView` registration
class MyViewController: UIViewController {
@IBOutlet private weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// This will register using the class (via `register(AnyClass?, forCellReuseIdentifier: String)`)
// because the CodeBasedCustomCell type conforms to Reusable, but not NibLoadable (nor the NibReusable typealias)
tableView.register(cellType: CodeBasedCustomCell.self)
// This will register using NibBasedCustomCell.xib (via `register(UINib?, forCellReuseIdentifier: String)`)
// because the NibBasedCustomCell type conforms to NibLoadable (via the NibReusable typealias)
tableView.register(cellType: NibBasedCustomCell.self)
}
}
3. Dequeue your cells
To dequeue a cell (typically in your cellForRowAtIndexPath
implementation), simply call dequeueReusableCell(indexPath:)
:
// Either
let cell = tableView.dequeueReusableCell(for: indexPath) as MyCustomCell
// Or
let cell: MyCustomCell = tableView.dequeueReusableCell(for: indexPath)
As long as Swift can use type-inference to understand that you'll want a cell of type MyCustomCell
(either using as MyCustomCell
or explicitly typing the receiving variable cell: MyCustomCell
), it will magically infer both the cell class to use and thus its reuseIdentifier
needed to dequeue the cell, and which exact type to return to save you a type-cast.
- No need for you to manipulate
reuseIdentifiers
Strings manually anymore! - No need to force-cast the returned
UITableViewCell
instance down to yourMyCustomCell
class either!
ð Example implementation of `cellForRowAtIndexPath` using `Reusable`
extension MyViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as CodeBasedCustomCell
// Customize the cell here. You can call any type-specific methods here without the need for type-casting
cell.fillWithText("Foo")
return cell
} else {
let cell = tableView.dequeueReusableCell(indexPath: indexPath) as NibBasedCustomCell
// Customize the cell here. no need to downcasting here either!
cell.fillWithImage(UIImage(named:"Bar"))
return cell
}
}
}
Now all you have is a beautiful code and type-safe cells, with compile-type checking, and no more String-based API!
ð¡ If the cell class you want to dequeue is computed at runtime and stored in a variable, you won't be able to use
as theVariable
orlet cell: theVariable
obviously. Instead, you can use the optional parametercellType
(which otherwise gets infered by the return type and is thus not necessary to provide explicitly)ð Example with a cell type determined at runtime
class ParentCell: UITableViewCell, Reusable {} class Child1Cell: ParentCell {} class Child2Cell: ParentCell {} func cellType(for indexPath: NSIndexPath) -> ParentCell.Type { return indexPath.row.isMultiple(of: 2) ? Child1Cell.self : Child2Cell.self } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cellClass = self.cellType(for: indexPath) // As `self.cellType(for:)` always returns a `ParentCell` (sub-)class, the type // of the variable `cell` below is infered to be `ParentCell` too. So only methods // declared in the parent `ParentCell` class will be accessible on the `cell` variable. // But this code will still dequeue the proper type of cell (Child1Cell or Child2Cell). let cell = tableView.dequeueReusableCell(for: indexPath, cellType: cellClass) // Then fill the content of your cell (using methods/properties from `ParentCell` type) return cell }
Type-safe XIB-based reusable views
Reusable
also allows you to create reusable custom views designed in Interface Builder to reuse them in other XIBs or Storyboards, or by code. This allows you to treat those views like custom UI widgets that can be used in multiple places in your app.
1. Declare your views to conform to NibLoadable
or NibOwnerLoadable
In your swift source declaring your custom view class:
- Use the
NibLoadable
protocol if the XIB you're using don't use its "File's Owner" and the reusable view you're designing is the root view of the XIB - Use the
NibOwnerLoadable
protocol if you used a "File's Owner" of the XIB being of the class of your reusable view, and the root view(s) of the XIB is to be set as a subview providing its content.
// a XIB-based custom UIView, used as root of the XIB
final class NibBasedRootView: UIView, NibLoadable { /* and that's it! */ }
// a XIB-based custom UIView, used as the XIB's "File's Owner"
final class NibBasedFileOwnerView: UIView, NibOwnerLoadable { /* and that's it! */ }
ð¡ You should use the second approach if you plan to use your custom view in another XIB or Storyboard.
This will allow you to just drop a UIView in a XIB/Storyboard and change its class in IB's inspector to the class of your custom XIB-based view to use it. That custom view will then automagically load its own content from the associated XIB when instantiated by the storyboard containing it, without having to write additional code to load the content of the custom view manually every time.
2. Design your view in Interface Builder
For example if you named your class MyCustomWidget
and made it NibOwnerLoadable
:
- Set the File's Owner's class to
MyCustomWidget
- Design the content of the view via the root view of that XIB (which is a standard
UIView
with no custom class) and its subviews - Connect any
@IBOutlets
and@IBActions
between the File's Owner (theMyCustomWidget
) and its content
ð¼ð A view configured to be `NibOwnerLoadable`
final class MyCustomWidget: UIView, NibOwnerLoadable {
@IBOutlet private var rectView: UIView!
@IBOutlet private var textLabel: UILabel!
@IBInspectable var rectColor: UIColor? {
didSet {
self.rectView.backgroundColor = self.rectColor
}
}
@IBInspectable var text: String? {
didSet {
self.textLabel.text = self.text
}
}
â¦
}
Then that widget can be integrated in a Storyboard Scene (or any other XIB) by simply dropping a UIView
on the Storyboard, and changing its class to MyCustomWidget
in IB's inspector.
ð¼ Example of a `NibOwnerLoadable` custom view once integrated in another Storyboard
- In the capture below, all blue square views have a custom class of
MyCustomWidget
set in Interface Builder. - When selecting one of these custom views, you have direct access to all
@IBOutlet
that thisMyCustomWidget
exposes, which allows you to connect them to other views of the Storyboard if needed - When selecting one of these custom views, you also have access to all the
@IBInspectable
properties. For example, in the capture below, you can see the "Rect color" and "Text" inspectable properties on the right panel, that you can change right from the Storyboard integrating your custom widget.
3a. Auto-loading the content of a NibOwnerLoadable
view
If you used NibOwnerLoadable
and made your custom view the File's Owner of your XIB, you should then override init?(coder:)
so that it loads it's associated XIB as subviews and add constraints automatically:
final class MyCustomWidget: UIView, NibOwnerLoadable {
â¦
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.loadNibContent()
}
}
self.loadNibContent()
is a method provided by the NibOwnerLoadable
mixin. It basically loads the content from the associated MyCustomWidget.xib
, then add all the root views in that XIB as subviews of your MyCustomWidget
, with appropriate layout constraints to make them the same size as your MyCustomWidget
container view.
Overriding init?(coder:)
and calling self.loadNibContent()
thus allows you to have that content automatically loaded by the system when that MyCustomWidget
in included in another XIB or in a Storyboard (as init?(coder:)
is the init
that is called by iOS to create those instances in a XIB or Storyboard)
ð¡ Note: it is also possible to override init(frame:)
similarly, in order to be able to also create an instance of that view manually via code if needed.
3b. Instantiating a NibLoadable
view
If you used NibLoadable
and made your custom view the root view of your XIB (not using the File's Owner at all), these are not designed to be used in other Storyboards or XIBs like NibOwnerLoadable
is, as they won't be able to auto-load their content.
Instead, you will instantiate those NibLoadable
views by code, which is as simple as calling loadFromNib()
on your custom class:
let view1 = NibBasedRootView.loadFromNib() // Create one instance
let view2 = NibBasedRootView.loadFromNib() // Create another one
let view3 = NibBasedRootView.loadFromNib() // and another one
â¦
Type-safe ViewControllers from Storyboards
Reusable
also allows you to mark your UIViewController
classes as StoryboardBased
or StoryboardSceneBased
to easily instantiate them from their associated Storyboard in a type-safe way.
1. Declare your UIViewController
to conform to StoryboardBased
or StoryboardSceneBased
In your swift source declaring your custom UIViewController
class:
- Use the
StoryboardBased
protocol if the*.storyboard
file has the same name as the ViewController's class, and its scene is the "initial scene" of the storyboard.- This is typically ideal if you use one Storyboard per ViewController, for example.
- Use the
StoryboardSceneBased
protocol if scene in your storyboard has the samesceneIdentifier
as the name of the ViewController's class, but the*.storyboard
file name doesn't necessary match the ViewController's class name.- This is typically ideal for secondary scenes in bigger storyboards
- You'll then be required to implement the
sceneStoryboard
type property to indicate the storyboard it belongs to.
ð Example of a ViewController being the initial ViewController of its Storyboard
In this example, CustomVC
is designed as the initial ViewController of a Storyboard named CustomVC.storyboard
:
final class CustomVC: UIViewController, StoryboardBased { /* and that's it! */ }
ð Example of a ViewController being an arbitrary scene in a differently-named Storyboard
In this example, SecondaryVC
is designed in a Storyboard name CustomVC.storyboard
(so with a different name than the class itself) and is not the initial ViewController, but instead has its "Scene Identifier" set to the value "SecondaryVC"
(same as the class name)
Conforming to StoryboardSceneBased
will still require you to implement static var sceneStoryboard: UIStoryboard { get }
to indicate the Storyboard where this scene is designed. You can typically implement that property using a let
type constant:
final class SecondaryVC: UIViewController, StoryboardSceneBased {
static let sceneStoryboard = UIStoryboard(name: "CustomVC", bundle: nil)
/* and that's it! */
}
2. Instantiate your UIViewControllers
Simply call instantiate()
on your custom class. This will automatically know which storyboard to load it from, and which scene (initial or not) to use to instantiate it.
func presentSecondary() {
let vc = SecondaryVC.instantiate() // Init from the "SecondaryVC" scene of CustomVC.storyboard
self.present(vc, animated: true) {}
}
Additional tips
Make your subclasses final
I advise you to mark your custom UITableViewCell
, UICollectionViewCell
, UIView
and UIViewController
subclasses as being final
. This is because:
- In most cases, the custom cells and VCs you plan to instantiate are not intended to be subclassed themselves.
- More importantly, it helps the compiler a lot and gives you big optimizations
- It can be required in some cases when conforming to
protocols
that haveSelf
requirements, like the ones used by this pod (Reusable
,StoryboardBased
, â¦).
In some cases you can avoid making your classes final
, but in general it's a good practice, and in the case of this pod, usually your custom UIViewController
or whatever won't be subclassed anyway:
- Either they are intended to be used and instantiated directly and never be subclassed, so
final
makes sense here - In case your custom
UIViewController
,UITableViewCell
, etc⦠is intended to be subclassed and be the parent class of many classes in your app, it makes more sense to add the protocol conformance (StoryboardBased
,Reusable
, â¦) to the child classes (and mark themfinal
) than adding the protocol on the parent, abstract class.
Customize reuseIdentifier, nib, etc for non-conventional uses
The protocols in this pod, like Reusable
, NibLoadable
, NibOwnerLoadable
, StoryboardBased
, NibReusable
⦠are what is usually called Mixins, which basically is a Swift protocol with a default implementation provided for all of its methods.
The main benefit is that you don't need to add any code: just conform to Reusable
, NibOwnerLoadable
or any of those protocol and you're ready to go with no additional code to write.
But of course, those provided implementations are just default implementations. That means that if you need you can still provide your own implementations in case for some reason some of your cells don't follow the classic configuration of using the same name for both the class, the reuseIdentifier
and the XIB file.
final class VeryCustomNibBasedCell: UITableViewCell, NibReusable {
// This cell use a non-standard configuration: its reuseIdentifier and XIB file
// have a different name as the class itself. So we need to provide a custom implementation or `NibReusable`
static var reuseIdentifier: String { return "VeryCustomReuseIdentifier" }
static var nib: UINib { return UINib(nibName: "VeryCustomUI", bundle: nil) } // Use VeryCustomUI.xib
// Then continue with the rest of your normal cell code
}
The same is true for all the protocols of this pod, which always provide default implementations which could still be replaced by your own if you need some custom cases.
But the beauty is in 90% of cases the default implementation will match typical conventions and the default implementations will be exactly what you want!
Type-safety and fatalError
Reusable
allows you to manipulate type-safe APIs and make you avoid typos. But things could still go wrong in case of a misconfguration, for example if you forgot to set the reuseIdentifier
of your cell in its XIB
, or you declared a FooViewController
to be StoryboardBased
but forgot to set the initial ViewController flag on that FooViewController
scene in that Storyboard, etc.
In such cases, because those are developer errors that should be caught as early as possible in the development process, Reusable
will call fatalError
with an error message as descriptive as possible (instead of crashing with an obscure message about some force-cast or force-unwrap or whatnot) to help you configure it right.
For example, if Reusable
fails to dequeue a cell, it will bail with a message like:
« Failed to dequeue a cell with identifier \(cellType.reuseIdentifier) matching type \(cellType.self). Check that the reuseIdentifier is set properly in your XIB/Storyboard and that you registered the cell beforehand. »
Hopefully, those explicit failure messages will allow you to understand what was misconfigured and help you fix it!
Example Project
This repository comes with an example project in the Example/
folder. Feel free to try it.
It demonstrates how Reusable
works for:
UITableViewCell
andUICollectionViewCell
subclasses,- Cells whose UI template is either only provided by plain code, or provided by a XIB, or prototyped directly in a Storyboard.
UICollectionView
'sSupplementaryViews
(section Headers)- Custom
UIView
designed in a XIB (NibOwnerLoadable
)
Talks and Articles about Reusable
The concepts behind Reusable has been presented in various articles and talks:
- Using Generics to improve TableView cells on my blog
- FrenchKit'16 talk: Mixins over Inheritance (video)
- Same talk was also given at NSSpain'16 (slides) and AppDevCon'17 (slides)
- No more String-typed instantiations on iOS Development
License
This code is distributed under the MIT license. See the LICENSE
file for more info.
Top Related Projects
🌲 Evergreen React UI Framework by Segment
Bootstrap components built with React
Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
An enterprise-class UI design language and React UI library
A utility-first CSS framework for rapid UI development.
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot