Stella contains a set of utilities that can be used during iOS development in Swift.
You can install Stella
using the Swift Package Manager. This is available starting from Xcode 11. Just search for icapps/ios-stella
and install it.
Stella is available through CocoaPods. To install it, simply add the following line to your Podfile
:
pod 'Stella', git: 'https://github.com/icapps/ios-stella.git', commit: '...'
Pass the correct commit reference to make sure your code doesn't break in future updates.
To integrate Stella into your Xcode project using Carthage, specify it in your Cartfile
"
github "icapps/ios-stella" ~> 1.5.1
Then, run the following command to build the Stella framework:
carthage update
Perform the additional steps in orde the get Carthage setup with this framework: Carthage's README
We have a cleaner way to use NSUserDefaults
. Define the user defaults by extending the DefaultsKeys
class.
extension DefaultsKeys {
// Writes a string object to the defaults with the 'stringValue' key.
static let stringValue = DefaultsKey<String?>("stringValue")
// Writes an integer to the defaults with the 'integerValue' key.
static let integerValue = DefaultsKey<Int?>("integerValue")
// Writes a double to the defaults with the 'doubleValue' key.
static let doubleValue = DefaultsKey<Double?>("doubleValue")
// Writes a float to the defaults with the 'floatValue' key.
static let floatValue = DefaultsKey<Float?>("floatValue")
// Writes a bool to the defaults with the 'booleanValue' key.
static let booleanValue = DefaultsKey<Bool?>("booleanValue")
// Writes a date object to the defaults with the 'dateValue' key.
static let dateValue = DefaultsKey<NSDate?>("dateValue")
// Writes a dictionary object to the defaults with the 'dictValue' key.
static let dictValue = DefaultsKey<[String: Any]?>("dictValue")
}
You can read/write the from/to the NSUserDefaults
by using the subscript
on the Defaults
class.
Defaults[.stringValue] = "A string value"
print(Defaults[.stringValue]) // Prints 'A string value'
Defaults[.integerValue] = 123
print(Defaults[.integerValue]) // Prints '123'
Defaults[.doubleValue] = 123.123
print(Defaults[.doubleValue]) // Prints '123.123'
Defaults[.floatValue] = 123.321
print(Defaults[.floatValue]) // Prints '123.312'
Defaults[.booleanValue] = true
print(Defaults[.booleanValue]) // Prints 'true'
Defaults[.dateValue] = NSDate()
print(Defaults[.dateValue]) // Prints '1996-12-19T16:39:57-08:00'
Defaults[.dictValue] = ["SomeKey" : "SomeValue"]
let value = Defaults[.dictValue]
print(value["SomeKey"]) // Prints 'SomeValue'
We have a cleaner way to use the Keychain
. Define the user defaults by extending the Keys
class.
extension Keys {
// Writes a string object to the keychain with the 'stringValue' key.
static let stringValue = Key<String?>("stringValue")
}
You can read/write the from/to the Keychain
by using the subscript
on the Keychain
class.
It supports both regular String
values as well as Codable
objects.
Keychain[.stringValue] = "A string value"
print(Keychain[.stringValue]) // Prints 'A string value'
```swift
Keychain[.codableValue] = ClassConformingToCodable()
print(Keychain[.codableValue]) // Prints '<ClassConformingToCodable: 0x0123456789>'
In some cases you want to be able to set additional keychain query paramaters on an item.
```swift
static let noBackupValue = Key<String?>("noBackup", {
return [kSecAttrAccessible as String: kSecAttrAccessibleAlwaysThisDeviceOnly]
}())
Localize a key in no time with this handy localization function.
let key = "this_is_your_localization_key"
print(key.localizedString)
// The debug console will print the localized
// string found in your .strings file.
Fetch an element from an array that could possible be out of bounds.
let array = [1, 2, 3, 4]
array[safe: 2] // Returns 3
arra[safe: 10] // Returns nil
Returns the array with a limited subset starting from the front/rear.
let array = [1, 2, 3, 4]
array.truncate(by: 2) // Returns [1, 2]
array.reverseTruncate(by: 2) // Returns [3, 4]
Returns if an element is found in an array.
let array = [1, 2, 3, 4]
array.contains(2) // Returns true
array.contains(10) // Returns false
Returns an array with unique values depending on the Hashable
value.
let array = [1, 2, 3, 2, 4, 1]
array.unique // Returns [1, 2, 3, 4]
Remove an element from an array and returns the removed index.
var array = [1, 2, 3, 4]
array.remove(3) // Returns 2
array // Mutated to [1, 2, 4]
Get the marketing and build version quickly from the bundle.
let bundle = Bundle.main
bundle.shortVersionString // Returns 1.2.3
bundle.bundleVersion // Returns 1 (the build version)
Quickly add a shadow around a certain view
let view = UIView()
view.layer.applyShadow()
let customShadowView = UIView()
customShadowView.layer.applyShadow(color: .red, opacity: 0.2, x: 0, y: 4, blur: 10, spread: 0)
Quickly remove a shadow from a certain view
let view = UIView()
view.layer.removeShadow()
Convert from degrees to radians and vice versa.
CGFloat(180).degreesToRadians // Returns .pi
CGFloat.pi.radiansToDegrees // Returns 180.0
Get the UIViewController
that manages your view.
let controller = UIViewController()
controller.view.respondingController // Returns the controller instance.
Register and reuse cells in a type safe way.
// Register a cell from a nib with the same name.
collectionView.register(CustomCollectionViewCell.self)
// Register a reusable view from a nib with the same name.
collectionView.register(CustomReusableView.self, forSupplementaryViewOfKind: "Some")
// Dequeue a cell of type CustomCollectionViewCell
collectionView.dequeueReusableCell(for: indexPath) as CustomCollectionViewCell
// Dequeue a reusable view of type CustomReusableView
collectionView.dequeueReusableSupplementaryView(ofKind: "Some", for: indexPath) as CustomReusableView
Register and reuse cells in a type safe way.
// Register a cell from a nib with the same name.
tableView.register(CustomTableViewCell.self)
// Register a footer view from a nib with the same name.
tableView.registerHeaderFooter(CustomFooterView.self)
// Dequeue a cell of type CustomTableViewCell
tableView.dequeueReusableCell(forIndexPath: indexPath) as CustomTableViewCell
// Dequeue a cell of type CustomTableViewCell with a custom identifier.
tableView.dequeueReusableCell(forIdentifier: "identifier") as CustomTableViewCell
// Dequeue a header view of type CustomReusableView with a custom identifier.
tableView.dequeueReusableHeaderFooter(forIdentifier: "identifier") as CustomReusableView
// Dequeue a header view of type CustomReusableView.
tableView.dequeueReusableHeaderFooter(forIdentifier: "identifier") as CustomReusableView
// Get the types cell for row.
tableView.cellForRow(at: indexPath) as CustomTableViewCell
Constraint a subview quickly to the bounds of the superview.
Optionally you can add insets that set some spacing inside the view.
view.constraint(to: superview)
view.constraint(to: superview, insets: .zero)
// Same as above but take safe area's into account.
view.constraint(to: superview, safeAreaInsets: .zero)
Get access to reuse identifiers.
// Return the name of the view's class as it's reuse identifier.
UIView.reuseIdenfier
// Return the name of the view's class as it's nib name.
UIView.nibName
// Return the nib matching the class name of the view.
UIView.nib
Load the UIView
from a nib with the same name.
It is important to type the destination property in orde to load the correct nib.
let view: SomeView = UIView.loadFromNib()
Easily add and remove a controller as a childViewController.
Optionally you can add insets that set some spacing inside the container view.
// Add the controller to the container view, pin it and handle the containment correctly.
rootController.add(childController: controller, to: containerView)
rootController.add(childController: controller, to: containerView, insets: .zero)
// Add the same child controller as above, but take the safe area's into account.
rootController.add(childController: controller, to: containerView, safeAreaInsets: .zero)
// Remove the controller and handle the containtment correctly.
rootController.remove(childController: controller)
Load the view controller directly from a storyboard.
// Load the initial controller from the storyboard.
let controller = SomeController.from(storyboard: "StoryboardName")
// Pass a custom bundle to load from.
let controller = SomeController.from(storyboard: "StoryboardName", bundle: CustomBundle())
// Load a controller with the given storyboard identifier.
let controller = SomeController.from(storyboard: "StoryboardName", identifier: "SomeControllerIdentifier")
Get the UIViewController
that is currently presented on top of the application. It doesn't matter if a UINavigationController
or a UITabBarController
is in play or not.
let controller = UIAlertController(...)
// Present an alert an top of all the controllers.
otherController.topMostViewController.present(controller, animated: false, completion: nil)
Added soms easy to used optional intializers.
URL(string: nil)
UIImage(data: nil)
- Add a Github issue describing the missing functionality or bug.
- Implement the changes according to the
Swiftlint
coding guidelines. - Make sure your changes don't break the current version. (
deprecate
is needed) - Fully test the added changes.
- Send a pull-request.
- Jelle Vandebeeck, @fousa
- Dylan Gyesbreghs, @dgyesbreghs
- Hans Van Herreweghe, @herre
- Hannes Van den Berghe, @HannesVDB
- Alain Hufkens, @hufkens
- Stijn Willems, @doozMen
Stella is available under the MIT license. See the LICENSE file for more info.