Skip to content

Commit

Permalink
Merge pull request #772 from badoo/unify-tree
Browse files Browse the repository at this point in the history
Unify tree
  • Loading branch information
wiruzx authored May 20, 2022
2 parents ef059d5 + 557fffe commit 948005c
Show file tree
Hide file tree
Showing 17 changed files with 921 additions and 918 deletions.
4 changes: 4 additions & 0 deletions ChattoAdditions/ChattoAdditions.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
20585095253D6FEF002B1153 /* ChatInputBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3C0CC051BFE496A0052747C /* ChatInputBar.xib */; };
20585098253D726C002B1153 /* ChattoAdditionsResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 20585088253D6F58002B1153 /* ChattoAdditionsResources.bundle */; };
2058509A253D7294002B1153 /* Bundle+ChattoAdditionsResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20585099253D7294002B1153 /* Bundle+ChattoAdditionsResources.swift */; };
2614F5452834E09D00F25AB0 /* MessageViewHierarchy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2614F5432834E09D00F25AB0 /* MessageViewHierarchy.swift */; };
261D26922505B99000973CD6 /* CALayer+ImageMask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261D26912505B99000973CD6 /* CALayer+ImageMask.swift */; };
261E7B4E278F4F3000757B08 /* ChatItemLifecycleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261E7B31278F4F3000757B08 /* ChatItemLifecycleViewModel.swift */; };
261E7B4F278F4F3000757B08 /* ViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261E7B32278F4F3000757B08 /* ViewFactoryProtocol.swift */; };
Expand Down Expand Up @@ -214,6 +215,7 @@
20585088253D6F58002B1153 /* ChattoAdditionsResources.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChattoAdditionsResources.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
2058508A253D6F58002B1153 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
20585099253D7294002B1153 /* Bundle+ChattoAdditionsResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+ChattoAdditionsResources.swift"; sourceTree = "<group>"; };
2614F5432834E09D00F25AB0 /* MessageViewHierarchy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageViewHierarchy.swift; sourceTree = "<group>"; };
261D26912505B99000973CD6 /* CALayer+ImageMask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+ImageMask.swift"; sourceTree = "<group>"; };
261E7B31278F4F3000757B08 /* ChatItemLifecycleViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatItemLifecycleViewModel.swift; sourceTree = "<group>"; };
261E7B32278F4F3000757B08 /* ViewFactoryProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewFactoryProtocol.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -482,6 +484,7 @@
261E7B40278F4F3000757B08 /* MultipleViewComposition.swift */,
261E7B41278F4F3000757B08 /* ViewModelBinding.swift */,
261E7B42278F4F3000757B08 /* Binder.swift */,
2614F5432834E09D00F25AB0 /* MessageViewHierarchy.swift */,
261E7B43278F4F3000757B08 /* ContainerViewProtocols.swift */,
261E7B44278F4F3000757B08 /* BaseViewComposition.swift */,
261E7B45278F4F3000757B08 /* ChatItemContentView.swift */,
Expand Down Expand Up @@ -1197,6 +1200,7 @@
261E7B56278F4F3000757B08 /* LayoutProviderFactoryProtocol.swift in Sources */,
C3C0CC621BFE496A0052747C /* TextChatInputItem.swift in Sources */,
C3C0CC3E1BFE496A0052747C /* TextMessagePresenterBuilder.swift in Sources */,
2614F5452834E09D00F25AB0 /* MessageViewHierarchy.swift in Sources */,
C3C0CC511BFE496A0052747C /* ChatInputBarAppearance.swift in Sources */,
C3C0CC2E1BFE496A0052747C /* BaseMessageCollectionViewCell.swift in Sources */,
C3C0CC5A1BFE496A0052747C /* PhotosInputCameraPicker.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public final class ChatItemPresenter<ChatItem>: ChatItemPresenterProtocol {
public func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) {
guard let cell = cell as? Cell else { fatalError() }
do {
let subviews = self.setupSubviews(of: cell)
let subviews = try self.setupSubviews(of: cell)
let viewModels = self.makeViewModels()
self.lifecycleObservers = viewModels.values.compactMap { $0 as? ChatItemLifecycleViewModel }
try self.binder.bind(subviews: subviews, viewModels: viewModels)
Expand Down Expand Up @@ -143,13 +143,13 @@ public final class ChatItemPresenter<ChatItem>: ChatItemPresenterProtocol {
}
}

private func setupSubviews(of cell: Cell) -> AnyIndexedSubviews {
private func setupSubviews(of cell: Cell) throws -> AnyIndexedSubviews {
if let indexed = cell.indexed {
return indexed
}

let subviews = self.factory.makeViews()
let indexed = self.assembler.assemble(subviews: subviews)
let indexed = try self.assembler.assemble(subviews: subviews)
cell.indexed = indexed
return indexed
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,11 @@

import UIKit

// MARK: - Single

public protocol SingleContainerViewProtocol {
func add(child view: UIView)
}

public struct DefaultSingleViewComposition<View: SingleContainerViewProtocol>: SingleViewComposition {
init() {}
public func add(child: UIView, to parent: View) {
parent.add(child: child)
}
}

extension ViewAssembler {
public mutating func register<Key: BindingKeyProtocol>(child: AnyBindingKey, parent: Key)
where Key.View: SingleContainerViewProtocol {
self.register(viewComposition: DefaultSingleViewComposition(), key: child, to: parent)
}

public mutating func register<Child: BindingKeyProtocol, Parent: BindingKeyProtocol>(child: Child, parent: Parent)
where Parent.View: SingleContainerViewProtocol {
self.register(child: .init(child), parent: parent)
}
}

// MARK: - Multiple

public protocol MultipleContainerViewProtocol {
func add(children: [UIView])
}

public struct DefaultMultipleViewComposition<View: MultipleContainerViewProtocol>: MultipleViewComposition {
init() {}
public func add(children: [UIView], to parent: View) {
parent.add(children: children)
}
}

extension ViewAssembler {
public mutating func register<Key: BindingKeyProtocol>(children: [AnyBindingKey], parent: Key)
where Key.View: MultipleContainerViewProtocol {
self.register(viewComposition: DefaultMultipleViewComposition(), keys: children, to: parent)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,39 +34,21 @@ public struct LayoutAssembler {
case layoutWasNotCalculated(Key)
}

private enum Composition {
case single(AnyBindingKey)
case multiple([AnyBindingKey])
}

// MARK: - Private properties

private let rootKey: AnyBindingKey
private var compositions: [AnyBindingKey: Composition] = [:]
private let hierarchy: MessageViewHierarchy
private var makeSizeProviders: [AnyBindingKey: (Any) throws -> AnySizeProvider] = [:]
private var makeCachingProviders: [AnyBindingKey: (Any) throws -> AnyCachedLayoutProvider] = [:]
private var layoutApplicators: [AnyBindingKey: (Any, Any) throws -> Void] = [:]

// MARK: - Instantiation

public init(rootKey: AnyBindingKey) {
self.rootKey = rootKey
}

public init<Root: BindingKeyProtocol>(rootKey: Root) {
self.init(rootKey: .init(rootKey))
public init(hierarchy: MessageViewHierarchy) {
self.hierarchy = hierarchy
}

// MARK: - Public API

public mutating func register<Parent: BindingKeyProtocol>(child: AnyBindingKey, for parent: Parent) {
self.compositions[.init(parent)] = .single(child)
}

public mutating func register<Parent: BindingKeyProtocol>(children: [AnyBindingKey], for parent: Parent) {
self.compositions[.init(parent)] = .multiple(children)
}

// TODO: Remove #746
public mutating func populateSizeProviders<Key: BindingKeyProtocol>(for key: Key)
where Key.LayoutProvider.Layout: SizeContainer, Key.View: ManualLayoutViewProtocol, Key.View.Layout == Key.LayoutProvider.Layout {
Expand All @@ -92,7 +74,7 @@ public struct LayoutAssembler {
}

public func assembleRootSizeProvider(layoutProviders: [Key: Any]) throws -> AnySizeProvider {
let key = self.rootKey
let key = self.hierarchy.root
var layoutCache: LayoutCache = [:]
return try self.assemble(for: key, with: layoutProviders, layoutCache: &layoutCache)
}
Expand All @@ -101,7 +83,7 @@ public struct LayoutAssembler {
public func applyLayout(with context: LayoutContext, views: [Key: Any], layoutProviders: [Key: Any]) throws {
var layoutCache: LayoutCache = [:]
// TODO: Check keys
let rootLayoutProvider = try self.assemble(for: self.rootKey, with: layoutProviders, layoutCache: &layoutCache)
let rootLayoutProvider = try self.assemble(for: self.hierarchy.root, with: layoutProviders, layoutCache: &layoutCache)

// perform layout to cache results
_ = try rootLayoutProvider.layout(for: context)
Expand Down Expand Up @@ -135,28 +117,30 @@ public struct LayoutAssembler {
}

let resultLayoutProvider: Any
switch self.compositions[key] {

case .single(let childKey)?:
switch try self.hierarchy.children(of: key) {
case .no:
resultLayoutProvider = layoutProvider
case .single(let childKey):
let child = try self.assemble(for: childKey, with: providers, layoutCache: &layoutCache)

guard var container = layoutProvider as? SingleContainerLayoutProviderProtocol else {
throw Error.internalInconsistency
}

container.childLayoutProvider = child
resultLayoutProvider = container
case .multiple(let childrenKeys):
let children: [AnySizeProvider] = try childrenKeys.map {
try self.assemble(for: $0, with: providers, layoutCache: &layoutCache)
}

case .multiple(let childrenKeys)?:
let children: [AnySizeProvider] = try childrenKeys
.map { try self.assemble(for: $0, with: providers, layoutCache: &layoutCache) }
guard var container = layoutProvider as? MultipleContainerLayoutProviderProtocol else {
throw Error.internalInconsistency
}

container.childrenLayoutProviders = children
resultLayoutProvider = container

case nil:
resultLayoutProvider = layoutProvider

}

let cachingContainer = try makeCachingProvider(resultLayoutProvider)
Expand Down Expand Up @@ -191,10 +175,3 @@ extension CachingLayoutProvider: AnyCachedLayoutProvider {
var lastPerformedLayoutResult: Any? { self.lastLayout }
}

// Convenience
extension LayoutAssembler {
public mutating func register<Child: BindingKeyProtocol, Parent: BindingKeyProtocol>(child: Child, for parent: Parent) {
self.register(child: .init(child), for: parent)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2015-present Badoo Trading Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.


public struct MessageViewHierarchy {

// MARK: - Type Declarations

public enum Children {
case no
case single(AnyBindingKey)
case multiple([AnyBindingKey])
}

enum AddError: Error {
case alreadyRegistered
}

// MARK: - Private properties

private var registeredChildren: [AnyBindingKey: Children] = [:]

// MARK: - Instantiation

public init(root: AnyBindingKey) {
self.root = root
}

// MARK: - Public API

// MARK: Registration

public mutating func add<ParentKey: BindingKeyProtocol>(child: AnyBindingKey, to parent: ParentKey) throws where ParentKey.View: SingleContainerViewProtocol {
try self.register(children: .single(child), to: parent)
}

public mutating func add<ParentKey: BindingKeyProtocol>(children: [AnyBindingKey], to parent: ParentKey) throws where ParentKey.View: MultipleContainerViewProtocol {
try self.register(children: .multiple(children), to: parent)
}

// MARK: Fetching

public let root: AnyBindingKey

public func children(of parent: AnyBindingKey) throws -> Children {
self.registeredChildren[parent] ?? .no
}

public func allRegistrations() -> [AnyBindingKey: Children] { self.registeredChildren }

// MARK: - Private methods

private mutating func register<ParentKey: BindingKeyProtocol>(children: Children, to parent: ParentKey) throws {
let erasedParent = AnyBindingKey(parent)
guard self.registeredChildren[erasedParent] == nil else { throw AddError.alreadyRegistered }
self.registeredChildren[erasedParent] = children
}
}

// Convenience
extension MessageViewHierarchy {

public mutating func add<ChildKey: BindingKeyProtocol, ParentKey: BindingKeyProtocol>(child: ChildKey, to parent: ParentKey) throws where ParentKey.View: SingleContainerViewProtocol {
try self.add(child: .init(child), to: parent)
}

public init<Key: BindingKeyProtocol>(root: Key) {
self.init(root: .init(root))
}
}

Loading

0 comments on commit 948005c

Please sign in to comment.