Skip to content

Commit 90d1806

Browse files
authored
Merge pull request #102 from fpillet/macOS-extensions
Added macOS extensions
2 parents d4559ac + bf3ee2a commit 90d1806

File tree

15 files changed

+1227
-789
lines changed

15 files changed

+1227
-789
lines changed

Action.xcodeproj/project.pbxproj

Lines changed: 225 additions & 38 deletions
Large diffs are not rendered by default.

Action.xcodeproj/xcshareddata/xcschemes/Action-macOS.xcscheme

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,37 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
language = ""
2930
shouldUseLaunchSchemeArgsEnv = "YES">
3031
<Testables>
32+
<TestableReference
33+
skipped = "NO">
34+
<BuildableReference
35+
BuildableIdentifier = "primary"
36+
BlueprintIdentifier = "3DD965BA1F5DC0E400C180FE"
37+
BuildableName = "macOS-Tests.xctest"
38+
BlueprintName = "macOS-Tests"
39+
ReferencedContainer = "container:Action.xcodeproj">
40+
</BuildableReference>
41+
</TestableReference>
3142
</Testables>
43+
<MacroExpansion>
44+
<BuildableReference
45+
BuildableIdentifier = "primary"
46+
BlueprintIdentifier = "1FCDDA811EAC3295006EB95B"
47+
BuildableName = "Action.framework"
48+
BlueprintName = "Action-macOS"
49+
ReferencedContainer = "container:Action.xcodeproj">
50+
</BuildableReference>
51+
</MacroExpansion>
3252
<AdditionalOptions>
3353
</AdditionalOptions>
3454
</TestAction>
3555
<LaunchAction
3656
buildConfiguration = "Debug"
3757
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
3858
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
59+
language = ""
3960
launchStyle = "0"
4061
useCustomWorkingDirectory = "NO"
4162
ignoresPersistentStateOnLaunch = "NO"

Action.xcodeproj/xcshareddata/xcschemes/Action.xcscheme

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
language = ""
2930
shouldUseLaunchSchemeArgsEnv = "YES">
3031
<Testables>
3132
<TestableReference
3233
skipped = "NO">
3334
<BuildableReference
3435
BuildableIdentifier = "primary"
3536
BlueprintIdentifier = "7F5E6A661D7F08D2000B6076"
36-
BuildableName = "Tests.xctest"
37-
BlueprintName = "Tests"
37+
BuildableName = "iOS-Tests.xctest"
38+
BlueprintName = "iOS-Tests"
3839
ReferencedContainer = "container:Action.xcodeproj">
3940
</BuildableReference>
4041
</TestableReference>
@@ -55,6 +56,7 @@
5556
buildConfiguration = "Debug"
5657
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
5758
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
59+
language = ""
5860
launchStyle = "0"
5961
useCustomWorkingDirectory = "NO"
6062
ignoresPersistentStateOnLaunch = "NO"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#if os(iOS) || os(tvOS) || os(macOS)
2+
import Foundation
3+
#if os(iOS) || os(tvOS)
4+
import UIKit
5+
public typealias Button = UIKit.UIButton
6+
#elseif os(macOS)
7+
import Cocoa
8+
public typealias Button = Cocoa.NSButton
9+
#endif
10+
import RxSwift
11+
import RxCocoa
12+
13+
public extension Reactive where Base: Button {
14+
/// Binds enabled state of action to button, and subscribes to rx_tap to execute action.
15+
/// These subscriptions are managed in a private, inaccessible dispose bag. To cancel
16+
/// them, set the rx.action to nil or another action.
17+
public var action: CocoaAction? {
18+
get {
19+
var action: CocoaAction?
20+
action = objc_getAssociatedObject(self.base, &AssociatedKeys.Action) as? Action
21+
return action
22+
}
23+
24+
set {
25+
// Store new value.
26+
objc_setAssociatedObject(self.base, &AssociatedKeys.Action, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
27+
28+
// This effectively disposes of any existing subscriptions.
29+
self.base.resetActionDisposeBag()
30+
31+
// Set up new bindings, if applicable.
32+
if let action = newValue {
33+
action
34+
.enabled
35+
.bind(to: self.isEnabled)
36+
.disposed(by: self.base.actionDisposeBag)
37+
38+
// Technically, this file is only included on tv/iOS platforms,
39+
// so this optional will never be nil. But let's be safe 😉
40+
let lookupControlEvent: ControlEvent<Void>?
41+
42+
#if os(tvOS)
43+
lookupControlEvent = self.primaryAction
44+
#elseif os(iOS) || os(macOS)
45+
lookupControlEvent = self.tap
46+
#endif
47+
48+
guard let controlEvent = lookupControlEvent else {
49+
return
50+
}
51+
52+
controlEvent
53+
.bind(to: action.inputs)
54+
.disposed(by: self.base.actionDisposeBag)
55+
}
56+
}
57+
}
58+
59+
/// Binds enabled state of action to button, and subscribes to rx_tap to execute action with given input transform.
60+
/// These subscriptions are managed in a private, inaccessible dispose bag. To cancel
61+
/// them, call bindToAction with another action or call unbindAction().
62+
public func bind<Input, Output>(to action: Action<Input, Output>, inputTransform: @escaping (Base) -> (Input)) {
63+
// This effectively disposes of any existing subscriptions.
64+
unbindAction()
65+
66+
// Technically, this file is only included on tv/iOS platforms,
67+
// so this optional will never be nil. But let's be safe 😉
68+
let lookupControlEvent: ControlEvent<Void>?
69+
70+
#if os(tvOS)
71+
lookupControlEvent = self.primaryAction
72+
#elseif os(iOS) || os(macOS)
73+
lookupControlEvent = self.tap
74+
#endif
75+
76+
guard let controlEvent = lookupControlEvent else {
77+
return
78+
}
79+
self.bind(to: action, controlEvent: controlEvent, inputTransform: inputTransform)
80+
}
81+
82+
/// Binds enabled state of action to button, and subscribes to rx_tap to execute action with given input value.
83+
/// These subscriptions are managed in a private, inaccessible dispose bag. To cancel
84+
/// them, call bindToAction with another action or call unbindAction().
85+
public func bind<Input, Output>(to action: Action<Input, Output>, input: Input) {
86+
self.bind(to: action) { _ in input }
87+
}
88+
}
89+
#endif
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Foundation
2+
#if os(iOS) || os(tvOS)
3+
import UIKit
4+
public typealias Control = UIKit.UIControl
5+
#elseif os(macOS)
6+
import Cocoa
7+
public typealias Control = Cocoa.NSControl
8+
#endif
9+
import RxSwift
10+
import RxCocoa
11+
12+
public extension Reactive where Base: Control {
13+
/// Binds enabled state of action to control, and subscribes action's execution to provided controlEvents.
14+
/// These subscriptions are managed in a private, inaccessible dispose bag. To cancel
15+
/// them, set the rx.action to nil or another action, or call unbindAction().
16+
public func bind<Input, Output>(to action: Action<Input, Output>, controlEvent: ControlEvent<Void>, inputTransform: @escaping (Base) -> (Input)) {
17+
// This effectively disposes of any existing subscriptions.
18+
unbindAction()
19+
20+
// For each tap event, use the inputTransform closure to provide an Input value to the action
21+
controlEvent
22+
.map { inputTransform(self.base) }
23+
.bind(to: action.inputs)
24+
.disposed(by: self.base.actionDisposeBag)
25+
26+
// Bind the enabled state of the control to the enabled state of the action
27+
action
28+
.enabled
29+
.bind(to: self.isEnabled)
30+
.disposed(by: self.base.actionDisposeBag)
31+
}
32+
33+
/// Unbinds any existing action, disposing of all subscriptions.
34+
public func unbindAction() {
35+
self.base.resetActionDisposeBag()
36+
}
37+
}

Sources/Action/UIKitExtensions/UIButton+Rx.swift

Lines changed: 0 additions & 85 deletions
This file was deleted.

Sources/Action/UIKitExtensions/UIControl+Rx.swift

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)