Skip to content

Instantly share code, notes, and snippets.

@ThePragmaticArt
Last active March 9, 2020 22:35
Show Gist options
  • Save ThePragmaticArt/6f98f3257fd239ddf76d82fd399a043f to your computer and use it in GitHub Desktop.
Save ThePragmaticArt/6f98f3257fd239ddf76d82fd399a043f to your computer and use it in GitHub Desktop.
import Foundation
import RealmSwift
public extension CodingUserInfoKey {
// Helper property to retrieve the context
static let realm = CodingUserInfoKey(rawValue: "realm")!
static let template = CodingUserInfoKey(rawValue: "template")!
}
public typealias DecodableManagedObject = DecoderUpdatable & ManagedObject & Decodable
public protocol CodingKeyIdentifiable: CodingKey {
static var idKey: Self { get }
}
public protocol DecoderUpdatable: ManagedObject {
associatedtype CodingKeys: CodingKeyIdentifiable
static func identifier(from decoder: Decoder) throws -> Self.ID
mutating func update(from decoder: Decoder) throws
}
extension DecoderUpdatable where ID: Decodable {
public static func identifier(from decoder: Decoder) throws -> Self.ID {
let container = try decoder.container(keyedBy: CodingKeys.self)
let identifier = try container.decode(ID.self, forKey: CodingKeys.idKey)
return identifier
}
}
public protocol DecodingFormat {
func decoder(for data: Data) -> Decoder
}
extension DecodingFormat {
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {
return try T.init(from: decoder(for: data))
}
}
extension DecodingFormat {
func update<T: DecoderUpdatable>(_ value: inout T, from data: Data) throws {
try value.update(from: decoder(for: data))
}
}
struct DecoderExtractor: Decodable {
let decoder: Decoder
init(from decoder: Decoder) throws {
self.decoder = decoder
}
}
extension JSONDecoder: DecodingFormat {
public func decoder(for data: Data) -> Decoder {
return try! decode(DecoderExtractor.self, from: data).decoder
}
public func decode<ManagedDatabaseObject: Object>(_ type: ManagedDatabaseObject.Type, from data: Data, into realm: Realm? = nil, createIfNecessary: Bool = true) throws -> ManagedDatabaseObject where ManagedDatabaseObject: DecodableManagedObject {
userInfo[.realm] = realm ?? userInfo[.realm]
guard let realm = userInfo[.realm] as? Realm else {
fatalError("Missing context")
}
let decoder = self.decoder(for: data)
let identifier = try ManagedDatabaseObject.identifier(from: decoder)
var object = createIfNecessary ? ((try? ManagedDatabaseObject.fetch(identifier, in: realm)) ?? ManagedDatabaseObject()) : try ManagedDatabaseObject.fetch(identifier, in: realm)
if object.id != identifier {
object.id = identifier
}
realm.add(object, update: .modified)
try object.update(from: decoder)
return object
}
public func decode<ManagedDatabaseObject: Object>(_ type: [ManagedDatabaseObject].Type, from data: Data, into realm: Realm? = nil, createIfNecessary: Bool = true) throws -> [ManagedDatabaseObject] where ManagedDatabaseObject: DecodableManagedObject {
userInfo[.realm] = realm ?? userInfo[.realm]
guard let realm = userInfo[.realm] as? Realm else {
fatalError("Missing context")
}
let decoder = self.decoder(for: data)
var container = try decoder.unkeyedContainer()
var objects = [ManagedDatabaseObject]()
while !container.isAtEnd {
let decoder = try container.superDecoder()
let identifier = try ManagedDatabaseObject.identifier(from: decoder)
var object = createIfNecessary ? ((try? ManagedDatabaseObject.fetch(identifier, in: realm)) ?? ManagedDatabaseObject()) : try ManagedDatabaseObject.fetch(identifier, in: realm)
if object.id != identifier {
object.id = identifier
}
realm.add(object, update: .modified)
try object.update(from: decoder)
objects.append(object)
}
return objects
}
}
extension PropertyListDecoder: DecodingFormat {
public func decoder(for data: Data) -> Decoder {
return try! decode(DecoderExtractor.self, from: data).decoder
}
}
extension KeyedDecodingContainer {
func update<T: DecoderUpdatable>(_ value: inout T, forKey key: Key, userInfo: [CodingUserInfoKey: Any] = [:]) throws {
let nestedDecoder = NestedDecoder(from: self, key: key, userInfo: userInfo)
try value.update(from: nestedDecoder)
}
public func update<ManagedDatabaseObject: Object>(_ type: ManagedDatabaseObject.Type, forKey key: Key, userInfo: [CodingUserInfoKey: Any] = [:], createIfNecessary: Bool = false) throws -> ManagedDatabaseObject where ManagedDatabaseObject: DecodableManagedObject {
guard let realm = userInfo[.realm] as? Realm else {
fatalError("Missing context")
}
let nestedDecoder = NestedDecoder(from: self, key: key, userInfo: userInfo)
let identifier = try ManagedDatabaseObject.identifier(from: nestedDecoder)
var object = createIfNecessary ? ((try? ManagedDatabaseObject.fetch(identifier, in: realm)) ?? ManagedDatabaseObject()) : try ManagedDatabaseObject.fetch(identifier, in: realm)
if object.id != identifier {
object.id = identifier
}
realm.add(object, update: .modified)
try object.update(from: nestedDecoder)
return object
}
public func update<ManagedDatabaseObject: Object>(_ type: [ManagedDatabaseObject].Type, forKey key: Key, userInfo: [CodingUserInfoKey: Any] = [:], createIfNecessary: Bool = false) throws -> [ManagedDatabaseObject] where Key: CodingKeyIdentifiable, ManagedDatabaseObject: DecodableManagedObject {
guard let realm = userInfo[.realm] as? Realm else {
fatalError("Missing context")
}
let nestedDecoder = NestedDecoder(from: self, key: key, userInfo: userInfo)
var container = try nestedDecoder.unkeyedContainer()
var objects = [ManagedDatabaseObject]()
while !container.isAtEnd {
let decoder = try container.superDecoder()
let identifier = try ManagedDatabaseObject.identifier(from: decoder)
var object = createIfNecessary ? ((try? ManagedDatabaseObject.fetch(identifier, in: realm)) ?? ManagedDatabaseObject()) : try ManagedDatabaseObject.fetch(identifier, in: realm)
do {
if object.id != identifier {
object.id = identifier
}
realm.add(object, update: .modified)
try object.update(from: decoder)
objects.append(object)
} catch {
if let realm = object.realm {
realm.delete(object)
}
}
}
return objects
}
}
import UIKit
///A default protocol adding a cell identifier to a structure
public protocol CellIdentifiable {
///The cell identifier used for dequeuing and registering cells
static var cellIdentifier: String { get }
}
///A default protocol adding a header/footer identifier to a structure
public protocol HeaderFooterIdentifiable {
///The header/footer identifier used for dequeuing and registering header/footer views
static var headerFooterIdentifier: String { get }
}
///A default protocol adding a storyboard identifier to a structure
public protocol StoryboardIdentifiable {
///The storyboard identifier used for dequeuing a view from a storyboard
static var storyboardIdentifier: String { get }
}
extension StoryboardIdentifiable where Self: NSObject {
public static var storyboardIdentifier: String {
return className
}
}
import Foundation
/// A protocol that provides demangled information regarding core Swift structure naming components in a usable way.
public protocol StructureNameReflectable {
/**
An ordered list of domain named based components for the structure.
- Example: A simple example would be:
```
class Dog: NSObject {
class Retriever: NSOjbect {}
}
let retriever = Retriever()
```
`retriever.structuredName` would output `["Dog", "Retriever"]`
*/
static var structureNameComponents: [String] { get }
///Outputs the structure's name in a string based form minus namespacing
static var structureName: String { get }
///Outputs the structure's name in a string based form with namespacing
static var namespacedStructureName: String { get }
///Outputs the bundle the structure is contained in
static var bundle: Bundle { get }
}
extension StructureNameReflectable {
public static var structureNameComponents: [String] {
let type = Mirror(reflecting: self).subjectType
let structureNameComponents = "\(type)".components(separatedBy: ".")
return structureNameComponents
}
public static var structureName: String {
var structureNameComponents = self.structureNameComponents
if structureNameComponents.count > 1 && structureNameComponents.last == "Type" {
structureNameComponents.removeLast()
}
return structureNameComponents.last!
}
public static var namespacedStructureName: String {
return structureNameComponents.joined(separator: ".")
}
}
extension StructureNameReflectable where Self: NSObject {
public static var bundle: Bundle {
return Bundle(for: self)
}
public static var className: String {
return structureName
}
}
extension NSObject: StructureNameReflectable { }
import UIKit
extension UIStoryboard {
public func instantiateViewController<ViewControllerClass: UIViewController>() -> ViewControllerClass {
return instantiateViewController(withIdentifier: ViewControllerClass.storyboardIdentifier) as! ViewControllerClass
}
@objc public func instantiateViewController(className: String, bundle: Bundle = .main) throws -> UIViewController {
guard let classType = bundle.classNamed(className) else {
throw NSError(domain: "com.storyboard", code: 404, userInfo: [NSLocalizedDescriptionKey: "Could not find class: \(className) in bundle: \(bundle)"])
}
guard let viewControllerClassType = classType as? StoryboardIdentifiable.Type else {
throw NSError(domain: "com.storyboard", code: 404, userInfo: [NSLocalizedDescriptionKey: "Object does not conform to StoryboardIdentifiable protocol"])
}
let viewController = instantiateViewController(withIdentifier: viewControllerClassType.storyboardIdentifier)
return viewController
}
}
public protocol StoryboardLoadable: StoryboardIdentifiable {
static func instantiateFromStoryboard<Class: UIViewController>(_ storyboard: String) -> Class
static func instantiateFromStoryboard<Class: UIViewController>() -> Class
static var designatedStoryboard: UIStoryboard { get }
static var designatedStoryboardName: String { get }
static var designatedStoryboardBundle: Bundle? { get }
}
extension StoryboardLoadable where Self: UIViewController {
public static func instantiateFromStoryboard<Class: UIViewController>() -> Class {
let viewController = instantiateFromStoryboard(name: designatedStoryboardName, bundle: designatedStoryboardBundle) as Class
return viewController
}
/// Instantiates a ViewController of the ViewController classed called from using the passed in storyboard name
///
/// - Parameter storyboard: A parameter provided to specify the storyboard name. Should be a constant value from UIStoryboardNames
/// - Returns: A ViewController loaded from the specified storyboard name. By default, this is loaded from the main bundle
public static func instantiateFromStoryboard<Class: UIViewController>(_ storyboard: String) -> Class {
let storyboard = UIStoryboard(name: storyboard, bundle: designatedStoryboardBundle)
return storyboard.instantiateViewController()
}
public static func instantiateFromStoryboard<Class: UIViewController>(name: String, bundle: Bundle?) -> Class {
let storyboard = UIStoryboard(name: name, bundle: bundle)
return storyboard.instantiateViewController()
}
}
extension UIViewController: StoryboardLoadable {
@objc open class var designatedStoryboard: UIStoryboard {
let storyboard = UIStoryboard(name: designatedStoryboardName, bundle: designatedStoryboardBundle)
return storyboard
}
@objc open class var designatedStoryboardBundle: Bundle? {
let bundle = Bundle(for: self)
return bundle
}
@objc open class var designatedStoryboardName: String {
return UIStoryboard.main.rawValue
}
}
import Foundation
@objc public protocol NavigationControllerAppearanceContainer: class {
@objc var childViewControllerForNavigationBarAppearance: UIViewController? { get }
@objc var preferredNavigationBarTintColor: UIColor? { get }
}
extension UIViewController {
@objc open var rightBarButtonItems: [UIBarButtonItem]? {
let currentNavigationController = parentNavigationController
return currentNavigationController?.navigationBar.topItem?.rightBarButtonItems
}
@objc open var childViewControllerForNavigationBarItems: UIViewController? {
return .none
}
@objc open func setNeedsToUpdateNavigationBar() {
guard let navigationController = parentNavigationController else {
return
}
var viewController = navigationController.childViewControllerForNavigationBarItems ?? self
while let currentChildViewControllerForNavigationBarItems = viewController.childViewControllerForNavigationBarItems {
viewController = currentChildViewControllerForNavigationBarItems
}
guard navigationController.topViewController == self else {
navigationController.navigationBar.topItem?.rightBarButtonItems = viewController.rightBarButtonItems
return
}
navigationItem.rightBarButtonItems = viewController.rightBarButtonItems
}
}
extension UIViewController {
@objc open func setNeedsNavigationBarAppearanceUpdate() {
guard let navigationController = parentNavigationController else {
return
}
navigationController.setNeedsNavigationBarAppearanceUpdate()
}
}
extension UIViewController: NavigationControllerAppearanceContainer {
@objc open var childViewControllerForNavigationBarAppearance: UIViewController? {
return .none
}
@objc open var preferredNavigationBarTintColor: UIColor? {
return .white
}
}
extension UINavigationController {
override open var childViewControllerForNavigationBarAppearance: UIViewController? {
var viewController = visibleViewController ?? self
if !viewControllers.contains(viewController) {
viewController = topViewController ?? self
}
while let currentChildViewControllerForNavigationBarAppearance = viewController.childViewControllerForNavigationBarAppearance {
viewController = currentChildViewControllerForNavigationBarAppearance
}
return viewController
}
open override func setNeedsNavigationBarAppearanceUpdate() {
guard let viewController = childViewControllerForNavigationBarAppearance else {
return
}
let barTintColor = viewController.preferredNavigationBarTintColor
navigationBar.barTintColor = barTintColor
navigationBar.isTranslucent = false
navigationBar.setNeedsLayout()
navigationBar.layoutIfNeeded()
navigationBar.setNeedsDisplay()
}
}
extension UINavigationController {
@objc open override var childViewControllerForNavigationBarItems: UIViewController? {
guard let visibleViewController = visibleViewController, viewControllers.contains(visibleViewController) else {
return topViewController
}
return visibleViewController
}
}
import Foundation
public protocol ChildViewControllerContainer {
var parentViewController: UIViewController? { get }
}
public protocol ContentContainerTraversable {
func contentContainer<T: UIContentContainer>() -> T?
}
public protocol ViewContainer {
func subviewsOfKind<ViewClass: UIView>(_ kind: ViewClass.Type) -> [ViewClass]
}
extension UIView: ViewContainer {
public func subviewsOfKind<ViewClass: UIView>(_ kind: ViewClass.Type) -> [ViewClass] {
guard !subviews.isEmpty else {
return [ViewClass]()
}
let views = subviews.reduce([ViewClass]()) { (subviews, subview) -> [ViewClass] in
guard let view = subview as? ViewClass else {
return subviews + subview.subviewsOfKind(ViewClass.self)
}
let views = subviews + [view] + view.subviewsOfKind(ViewClass.self)
return views
}
return views
}
}
extension UIView: ContentContainerTraversable {
public func contentContainer<T: UIContentContainer>() -> T? {
var responder = next
while let currentResponder = responder {
guard responder is T else {
responder = currentResponder.next
continue
}
break
}
return responder as? T
}
}
extension UIView: ChildViewControllerContainer {
@objc public weak var parentViewController: UIViewController? {
return contentContainer() as UIViewController?
}
}
extension UITableViewCell {
@objc public weak var tableViewController: UITableViewController? {
return contentContainer() as UITableViewController?
}
}
extension UICollectionViewCell {
@objc public weak var collectionViewController: UICollectionViewController? {
return contentContainer() as UICollectionViewController?
}
}
extension UIViewController {
@objc public weak var topParent: UIViewController? {
var parent = self.parent
while let currentParent = parent {
guard let newParent = currentParent.parent else {
break
}
parent = newParent
}
return parent
}
}
extension UIViewController {
@objc public weak var parentNavigationController: UINavigationController? {
return parentViewController() as UINavigationController?
}
@nonobjc public func parentViewController<T: UIViewController>() -> T? {
var parentViewController = parent
while let currentParentViewController = parentViewController {
guard presentingViewController != currentParentViewController else {
break
}
guard parentViewController is T else {
parentViewController = currentParentViewController.parent
continue
}
break
}
return parentViewController as? T
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment