Skip to content

Commit d2dc092

Browse files
committed
Naming changes.
DistinctiveReuse - removed. rx.reuseBag - setter removed. rx.viewModel - binder added.
1 parent 121d657 commit d2dc092

14 files changed

+192
-102
lines changed

Diff for: CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [2.0.0](https://github.com/sinarionn/ReusableView/releases/tag/2.0.0)
6+
7+
Naming changes.
8+
DistinctiveReuse - removed.
9+
rx.reuseBag - setter removed.
10+
rx.viewModel - binder added.
11+
12+
513
## [1.2.1](https://github.com/sinarionn/ReusableView/releases/tag/1.2.1)
614

715
Fixed bug with structures hidden behind protocols

Diff for: Cartfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
github "ReactiveX/RxSwift"
1+
github "ReactiveX/RxSwift" ~> 4.0

Diff for: Cartfile.resolved

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github "ReactiveX/RxSwift" "4.2.0"

Diff for: Carthage/Checkouts/RxSwift

Submodule RxSwift updated 978 files

Diff for: Plists/ReusableView.plist

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.2.1</string>
18+
<string>2.0.0</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Diff for: README.md

+24-23
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- osX 10.10+
1313
- Xcode 9+
1414
- Swift 4
15+
- RxCocoa 4.0+
1516

1617
## Installation
1718

@@ -25,46 +26,46 @@ pod 'ReusableView'
2526
## Usage
2627

2728
Extend your class with one of the following protocols and get .viewModel property for free. )
28-
Each viewModel change will release previous subscriptions (by releasing previous disposeBag) call `onUpdate` method again.
29+
Each viewModel change releases previous subscriptions (by releasing previous reuseBag) and calls `onUpdate` method again.
2930

30-
**NonReusableViewProtocol** - if your view should not be reused. All next attempts to set viewModel will only call onAttemptToReuse method.
31+
**NonReusableType** - if your view should not be reused. All next attempts to set viewModel will only call onAttemptToReuse method. (Allows you to ensure vm will be only one. Usually used with UIViewControllers.)
3132

32-
**ReusableViewProtocol** - if your view supports reuse. prepareForReuse will be called each time.
33+
**ReusableType** - if your view supports reuse. viewModelWillUpdate will be called before each assignment. (can be used with cells, views in stackview and so on)
34+
35+
36+
## Methods
37+
38+
**prepareForUsage()** - called only once before first assignment, can be used to initialize view. (check out default implementations)
39+
40+
**viewModelWillUpdate()** - called before each assignment.
3341

34-
**DistinctiveReuse** - marker, ReusableView will ignore all viewModels that are equal to current one.
3542

3643
## Examples
3744

38-
#### Simple NonReusableView
3945

4046
```swift
41-
protocol ViewModelProtocol {
42-
var observable: Observable<String> { get }
47+
protocol MainViewModelType {
48+
var child: Driver<ChildViewModelType> { get }
4349
}
4450

45-
class NonReusableView: UIView, NonReusableViewProtocol {
46-
@IBOutlet weak var label: UILabel!
47-
48-
// In case of NonReusableViewProtocol disposeBag are equal to rx.disposeBag. Method will be called only one time.
49-
func onUpdate(with viewModel: ViewModelProtocol, disposeBag: DisposeBag) {
50-
viewModel.observable.bindTo(label.rx.text).addDisposableTo(disposeBag)
51-
}
51+
protocol ChildViewModelType {
52+
var title: Driver<String> { get }
5253
}
53-
```
5454

55-
#### Simplest ReusableView
55+
class MainViewController: UIViewController, NonReusableType {
56+
@IBOutlet weak var childView: ChildView!
5657

57-
```swift
58-
protocol ViewModelProtocol: Equatable {
59-
var observable: Observable<String> { get }
58+
func onUpdate(with viewModel: MainViewModelType, reuseBag: DisposeBag) {
59+
viewModel.child.drive(childView.rx.viewModel).disposed(by: reuseBag)
60+
}
6061
}
6162

62-
class ReusableView: UIView, ReusableViewProtocol, DistinctiveReuse {
63+
class ChildView: UIView, ReusableType {
6364
@IBOutlet weak var label: UILabel!
6465

65-
// parameter disposeBag will be new for each time viewModel is successfully set.
66-
func onUpdate(with viewModel: ViewModelProtocol, disposeBag: DisposeBag) {
67-
viewModel.observable.bindTo(label.rx.text).addDisposableTo(disposeBag)
66+
// parameter reuseBag will be new for each new viewModel.
67+
func onUpdate(with viewModel: ChildViewModelType, reuseBag: DisposeBag) {
68+
viewModel.title.drive(label.rx.text).disposed(by: reuseBag)
6869
}
6970
}
7071
```

Diff for: ReusableView.podspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Pod::Spec.new do |s|
33
s.name = "ReusableView"
4-
s.version = "1.2.1"
4+
s.version = "2.0.0"
55
s.summary = "Reusable and NonReusable viewModel containers"
66

77
s.homepage = "https://github.com/sinarionn/ReusableView"
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
1313
s.osx.deployment_target = '10.10'
1414
s.source = { :git => "https://github.com/sinarionn/ReusableView.git", :tag => s.version.to_s }
1515
s.requires_arc = true
16-
s.dependency 'RxSwift' , '~> 4.0'
16+
s.dependency 'RxCocoa' , '~> 4.0'
1717

1818
s.source_files = 'Sources/*.swift'
1919
end

Diff for: ReusableView.xcodeproj/project.pbxproj

+39-13
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
C0A128151D59C7820057F9E8 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0A127E31D59C0440057F9E8 /* RxCocoa.framework */; };
1111
C0A128161D59C7860057F9E8 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0A127DB1D59C0440057F9E8 /* RxSwift.framework */; };
1212
C0ADC4F81E476829001D889B /* ReusableView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0D5E8C31E4731B900860A05 /* ReusableView.framework */; };
13-
C0ADC4FA1E47743E001D889B /* ViewModelHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ADC4F91E47743E001D889B /* ViewModelHolderProtocol.swift */; };
14-
C0D5E8D21E4733C300860A05 /* NonReusableViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8D11E4733C200860A05 /* NonReusableViewProtocol.swift */; };
13+
C0ADC4FA1E47743E001D889B /* ViewModelHolderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ADC4F91E47743E001D889B /* ViewModelHolderType.swift */; };
14+
C0D210742101DC0D006111C9 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0A127E31D59C0440057F9E8 /* RxCocoa.framework */; };
15+
C0D5E8D21E4733C300860A05 /* NonReusableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8D11E4733C200860A05 /* NonReusableType.swift */; };
1516
C0D5E8D41E47340300860A05 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8D31E47340300860A05 /* Helpers.swift */; };
16-
C0D5E8D61E47356C00860A05 /* ReusableViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8D51E47356C00860A05 /* ReusableViewProtocol.swift */; };
17+
C0D5E8D61E47356C00860A05 /* ReusableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8D51E47356C00860A05 /* ReusableType.swift */; };
1718
C0D5E8DA1E473ED600860A05 /* AssociationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8D91E473ED600860A05 /* AssociationsTests.swift */; };
1819
C0D5E8DD1E474F5700860A05 /* NonReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8DC1E474F5700860A05 /* NonReusableViewTests.swift */; };
1920
C0D5E8DF1E47534900860A05 /* ReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D5E8DE1E47534900860A05 /* ReusableViewTests.swift */; };
@@ -161,6 +162,13 @@
161162
remoteGlobalIDString = C85BA04B1C3878740075D68E;
162163
remoteInfo = PerformanceTests;
163164
};
165+
C0BD34DF2101E2EB00513C86 /* PBXContainerItemProxy */ = {
166+
isa = PBXContainerItemProxy;
167+
containerPortal = C0A127C31D59C0440057F9E8 /* Rx.xcodeproj */;
168+
proxyType = 2;
169+
remoteGlobalIDString = C8E8BA551E2C181A00A4AC2C;
170+
remoteInfo = Benchmarks;
171+
};
164172
/* End PBXContainerItemProxy section */
165173

166174
/* Begin PBXFileReference section */
@@ -170,12 +178,12 @@
170178
9A4F5FE91BF89C4800E731F4 /* Tests.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Tests.plist; path = Plists/Tests.plist; sourceTree = "<group>"; };
171179
C089D3271D54A3BF00599DD7 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
172180
C0A127C31D59C0440057F9E8 /* Rx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Rx.xcodeproj; path = Carthage/Checkouts/RxSwift/Rx.xcodeproj; sourceTree = "<group>"; };
173-
C0ADC4F91E47743E001D889B /* ViewModelHolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModelHolderProtocol.swift; sourceTree = "<group>"; };
181+
C0ADC4F91E47743E001D889B /* ViewModelHolderType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModelHolderType.swift; sourceTree = "<group>"; };
174182
C0D5E88C1E472F0600860A05 /* ReusableView.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = ReusableView.plist; path = Plists/ReusableView.plist; sourceTree = "<group>"; };
175183
C0D5E8C31E4731B900860A05 /* ReusableView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReusableView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
176-
C0D5E8D11E4733C200860A05 /* NonReusableViewProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonReusableViewProtocol.swift; sourceTree = "<group>"; };
184+
C0D5E8D11E4733C200860A05 /* NonReusableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonReusableType.swift; sourceTree = "<group>"; };
177185
C0D5E8D31E47340300860A05 /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
178-
C0D5E8D51E47356C00860A05 /* ReusableViewProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableViewProtocol.swift; sourceTree = "<group>"; };
186+
C0D5E8D51E47356C00860A05 /* ReusableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableType.swift; sourceTree = "<group>"; };
179187
C0D5E8D91E473ED600860A05 /* AssociationsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssociationsTests.swift; path = Tests/AssociationsTests.swift; sourceTree = SOURCE_ROOT; };
180188
C0D5E8DC1E474F5700860A05 /* NonReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NonReusableViewTests.swift; path = Tests/NonReusableViewTests.swift; sourceTree = SOURCE_ROOT; };
181189
C0D5E8DE1E47534900860A05 /* ReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReusableViewTests.swift; path = Tests/ReusableViewTests.swift; sourceTree = SOURCE_ROOT; };
@@ -197,6 +205,7 @@
197205
isa = PBXFrameworksBuildPhase;
198206
buildActionMask = 2147483647;
199207
files = (
208+
C0D210742101DC0D006111C9 /* RxCocoa.framework in Frameworks */,
200209
C0D5E8E11E47555000860A05 /* RxSwift.framework in Frameworks */,
201210
);
202211
runOnlyForDeploymentPostprocessing = 0;
@@ -233,6 +242,7 @@
233242
9AC8C9F11969BBB5006F1EAC /* Code */,
234243
9AC8C9FF1969BBB5006F1EAC /* Tests */,
235244
9AC8C9F01969BBB5006F1EAC /* Products */,
245+
C0D2105F2101DC0D006111C9 /* Frameworks */,
236246
);
237247
sourceTree = "<group>";
238248
};
@@ -248,9 +258,9 @@
248258
9AC8C9F11969BBB5006F1EAC /* Code */ = {
249259
isa = PBXGroup;
250260
children = (
251-
C0ADC4F91E47743E001D889B /* ViewModelHolderProtocol.swift */,
252-
C0D5E8D11E4733C200860A05 /* NonReusableViewProtocol.swift */,
253-
C0D5E8D51E47356C00860A05 /* ReusableViewProtocol.swift */,
261+
C0ADC4F91E47743E001D889B /* ViewModelHolderType.swift */,
262+
C0D5E8D11E4733C200860A05 /* NonReusableType.swift */,
263+
C0D5E8D51E47356C00860A05 /* ReusableType.swift */,
254264
C0D5E8D31E47340300860A05 /* Helpers.swift */,
255265
);
256266
name = Code;
@@ -291,10 +301,18 @@
291301
C0A127FD1D59C0440057F9E8 /* AllTests-tvOS.xctest */,
292302
C0A127FF1D59C0440057F9E8 /* AllTests-macOS.xctest */,
293303
C0A128011D59C0440057F9E8 /* PerformanceTests.app */,
304+
C0BD34E02101E2EB00513C86 /* Benchmarks.xctest */,
294305
);
295306
name = Products;
296307
sourceTree = "<group>";
297308
};
309+
C0D2105F2101DC0D006111C9 /* Frameworks */ = {
310+
isa = PBXGroup;
311+
children = (
312+
);
313+
name = Frameworks;
314+
sourceTree = "<group>";
315+
};
298316
/* End PBXGroup section */
299317

300318
/* Begin PBXHeadersBuildPhase section */
@@ -528,10 +546,18 @@
528546
C0A128011D59C0440057F9E8 /* PerformanceTests.app */ = {
529547
isa = PBXReferenceProxy;
530548
fileType = wrapper.application;
531-
path = PerformanceTests.app;
549+
name = PerformanceTests.app;
550+
path = Microoptimizations.app;
532551
remoteRef = C0A128001D59C0440057F9E8 /* PBXContainerItemProxy */;
533552
sourceTree = BUILT_PRODUCTS_DIR;
534553
};
554+
C0BD34E02101E2EB00513C86 /* Benchmarks.xctest */ = {
555+
isa = PBXReferenceProxy;
556+
fileType = wrapper.cfbundle;
557+
path = Benchmarks.xctest;
558+
remoteRef = C0BD34DF2101E2EB00513C86 /* PBXContainerItemProxy */;
559+
sourceTree = BUILT_PRODUCTS_DIR;
560+
};
535561
/* End PBXReferenceProxy section */
536562

537563
/* Begin PBXResourcesBuildPhase section */
@@ -566,10 +592,10 @@
566592
isa = PBXSourcesBuildPhase;
567593
buildActionMask = 2147483647;
568594
files = (
569-
C0ADC4FA1E47743E001D889B /* ViewModelHolderProtocol.swift in Sources */,
570-
C0D5E8D21E4733C300860A05 /* NonReusableViewProtocol.swift in Sources */,
595+
C0ADC4FA1E47743E001D889B /* ViewModelHolderType.swift in Sources */,
596+
C0D5E8D21E4733C300860A05 /* NonReusableType.swift in Sources */,
571597
C0D5E8D41E47340300860A05 /* Helpers.swift in Sources */,
572-
C0D5E8D61E47356C00860A05 /* ReusableViewProtocol.swift in Sources */,
598+
C0D5E8D61E47356C00860A05 /* ReusableType.swift in Sources */,
573599
);
574600
runOnlyForDeploymentPostprocessing = 0;
575601
};

Diff for: Sources/Helpers.swift

+15-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import Foundation
1010
import RxSwift
11+
import RxCocoa
1112

1213
internal func associate(_ object: Any?, withValue value: Any?, by key: UnsafeRawPointer, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
1314
object.map{ objc_setAssociatedObject($0, key, value.map(AssociationWrapper.init), policy) }
@@ -18,7 +19,7 @@ internal func associated<T>(with object: Any, by key: UnsafeRawPointer) -> T? {
1819
return wrapper?.value as? T
1920
}
2021

21-
// unfortunately i was forced to start using such wrappers due to a new bug with structures hidden behind protocols
22+
// unfortunately i was forced to start using such wrappers due to a new objc bug with structures hidden behind protocols
2223
internal class AssociationWrapper {
2324
let value: Any
2425

@@ -42,24 +43,21 @@ extension Reactive where Base: AnyObject {
4243
}
4344
}
4445

45-
extension Reactive where Base: ReusableViewProtocol {
46+
extension Reactive where Base: ReusableType {
4647
public var reuseBag: DisposeBag {
4748
get {
4849
return base.reuseBag
4950
}
50-
nonmutating set {
51-
base.reuseBag = newValue
52-
}
5351
}
5452
}
5553

56-
extension ViewModelHolderProtocol {
57-
internal var _viewModelDidUpdate: PublishSubject<(ViewModelProtocol, DisposeBag)> {
54+
extension ViewModelHolderType {
55+
internal var _viewModelDidUpdate: PublishSubject<(ViewModelType, DisposeBag)> {
5856
get {
5957
objc_sync_enter(self)
6058
defer { objc_sync_exit(self) }
61-
guard let existingObserver : PublishSubject<(ViewModelProtocol, DisposeBag)> = associated(with: self, by: &AssociatedKeys.viewModelUpdateObserver) else {
62-
let newObserver = PublishSubject<(ViewModelProtocol, DisposeBag)>()
59+
guard let existingObserver : PublishSubject<(ViewModelType, DisposeBag)> = associated(with: self, by: &AssociatedKeys.viewModelUpdateObserver) else {
60+
let newObserver = PublishSubject<(ViewModelType, DisposeBag)>()
6361
associate(self, withValue: newObserver, by: &AssociatedKeys.viewModelUpdateObserver)
6462
return newObserver
6563
}
@@ -68,10 +66,16 @@ extension ViewModelHolderProtocol {
6866
}
6967
}
7068

71-
extension Reactive where Base: ViewModelHolderProtocol {
72-
public var viewModelDidUpdate: Observable<(Base.ViewModelProtocol, DisposeBag)> {
69+
extension Reactive where Base: ViewModelHolderType {
70+
public var viewModelDidUpdate: Observable<(Base.ViewModelType, DisposeBag)> {
7371
return base._viewModelDidUpdate.asObservable()
7472
}
73+
74+
public var viewModel: Binder<Base.ViewModelType?> {
75+
return Binder(base){ holder, viewModel in
76+
holder.viewModel = viewModel
77+
}
78+
}
7579
}
7680

7781
fileprivate struct AssociatedKeys {

Diff for: Sources/NonReusableViewProtocol.swift renamed to Sources/NonReusableType.swift

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// NonReusableViewProtocol.swift
2+
// NonReusableType.swift
33
// ReusableView
44
//
55
// Created by Artem Antihevich on 2/5/17.
@@ -9,29 +9,29 @@
99
import Foundation
1010
import RxSwift
1111

12-
public protocol NonReusableViewProtocol: ViewModelHolderProtocol {
13-
func onAttemptToReuse(with viewModel: ViewModelProtocol?)
12+
public protocol NonReusableType: ViewModelHolderType {
13+
func onAttemptToReuse(with viewModel: ViewModelType?)
1414
}
1515

16-
extension NonReusableViewProtocol {
17-
public func onAttemptToReuse(with viewModel: ViewModelProtocol?) {
18-
print("\(String(describing: self)) doesn't support reuse. Use ReusableViewProtocol instead.")
16+
extension NonReusableType {
17+
public func onAttemptToReuse(with viewModel: ViewModelType?) {
18+
assertionFailure("\(String(describing: self)) doesn't support reuse. Use ReusableType instead.")
1919
}
2020
}
2121

22-
extension NonReusableViewProtocol where Self.CompatibleType: AnyObject {
23-
public var viewModel: ViewModelProtocol? {
22+
extension NonReusableType where Self.CompatibleType: AnyObject {
23+
public var viewModel: ViewModelType? {
2424
set {
2525
objc_sync_enter(self); defer { objc_sync_exit(self) }
26-
guard associated(with: self, by: &AssociatedKeys.viewModel) as ViewModelProtocol? == nil else {
26+
guard associated(with: self, by: &AssociatedKeys.viewModel) as ViewModelType? == nil else {
2727
return onAttemptToReuse(with: newValue)
2828
}
2929
guard let newVM = newValue else { return }
3030
prepareForUsage()
3131
associate(self, withValue: newVM, by: &AssociatedKeys.viewModel)
3232
objc_sync_exit(self)
3333

34-
onUpdate(with: newVM, disposeBag: rx.disposeBag)
34+
onUpdate(with: newVM, reuseBag: rx.disposeBag)
3535
_viewModelDidUpdate.onNext((newVM, rx.disposeBag))
3636
}
3737

0 commit comments

Comments
 (0)