Skip to content

Commit 6ef1e94

Browse files
authored
Merge pull request #150 from spotify/connectable-map
Add `Connectable.map` to `MobiusExtras`
2 parents 3c8c7c0 + a031592 commit 6ef1e94

File tree

4 files changed

+122
-13
lines changed

4 files changed

+122
-13
lines changed

Mobius.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
025BB529244DA65100E80BD2 /* ConnectableMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 025BB528244DA65100E80BD2 /* ConnectableMap.swift */; };
11+
025BB52C244DA6BF00E80BD2 /* ConnectableMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 025BB52A244DA68500E80BD2 /* ConnectableMapTests.swift */; };
12+
025BB52D244DA6C400E80BD2 /* ConnectableMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 025BB528244DA65100E80BD2 /* ConnectableMap.swift */; };
1013
02BED1BB21DD20D20093FB47 /* ConnectableContramap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9CE80421197FE000DB79A7 /* ConnectableContramap.swift */; };
1114
02C4061D2373078400BD7ED8 /* EffectExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C4061C2373078400BD7ED8 /* EffectExecutor.swift */; };
1215
02C40621237422EF00BD7ED8 /* EffectRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C40620237422EF00BD7ED8 /* EffectRouter.swift */; };
@@ -292,6 +295,8 @@
292295
/* End PBXCopyFilesBuildPhase section */
293296

294297
/* Begin PBXFileReference section */
298+
025BB528244DA65100E80BD2 /* ConnectableMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectableMap.swift; sourceTree = "<group>"; };
299+
025BB52A244DA68500E80BD2 /* ConnectableMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectableMapTests.swift; sourceTree = "<group>"; };
295300
02C4061C2373078400BD7ED8 /* EffectExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectExecutor.swift; sourceTree = "<group>"; };
296301
02C40620237422EF00BD7ED8 /* EffectRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectRouter.swift; sourceTree = "<group>"; };
297302
02C40622237425E100BD7ED8 /* EffectRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectRouterTests.swift; sourceTree = "<group>"; };
@@ -754,6 +759,7 @@
754759
5BB287E8209995420043B530 /* SimpleLogger.swift */,
755760
DD748322212DB525008EEECD /* Copyable.swift */,
756761
5B1F104C21105CAD0067193C /* EventSource+Extensions.swift */,
762+
025BB528244DA65100E80BD2 /* ConnectableMap.swift */,
757763
);
758764
path = Source;
759765
sourceTree = "<group>";
@@ -847,6 +853,7 @@
847853
5B9CE80621199D4400DB79A7 /* ConnectableContramapTests.swift */,
848854
DD748324212DEEC1008EEECD /* CopyableTests.swift */,
849855
5B1F104F21105CC00067193C /* EventSource+ExtensionsTests.swift */,
856+
025BB52A244DA68500E80BD2 /* ConnectableMapTests.swift */,
850857
2DA1E89D2449F1ED00D240B7 /* WikiTutorialTest.swift */,
851858
);
852859
path = Test;
@@ -1252,6 +1259,7 @@
12521259
3EE5AF052110BF2E00CF8CA8 /* ConnectableClass.swift in Sources */,
12531260
2DF4C42420DBDF1B00A4B6DE /* SimpleLogger.swift in Sources */,
12541261
02BED1BB21DD20D20093FB47 /* ConnectableContramap.swift in Sources */,
1262+
025BB52D244DA6C400E80BD2 /* ConnectableMap.swift in Sources */,
12551263
5B1F104E21105CB10067193C /* EventSource+Extensions.swift in Sources */,
12561264
2D3A696D2355AED90053C95E /* Lock.swift in Sources */,
12571265
);
@@ -1357,6 +1365,7 @@
13571365
5B9CE80521197FE000DB79A7 /* ConnectableContramap.swift in Sources */,
13581366
5B1F104D21105CAD0067193C /* EventSource+Extensions.swift in Sources */,
13591367
DDA64A6720A0AD2D00150355 /* SimpleLogger.swift in Sources */,
1368+
025BB529244DA65100E80BD2 /* ConnectableMap.swift in Sources */,
13601369
5BCF5F0120F3620700721C0D /* ConnectableClass.swift in Sources */,
13611370
2D3A696E2355AED90053C95E /* Lock.swift in Sources */,
13621371
);
@@ -1419,6 +1428,7 @@
14191428
buildActionMask = 2147483647;
14201429
files = (
14211430
DD748325212DEEC1008EEECD /* CopyableTests.swift in Sources */,
1431+
025BB52C244DA6BF00E80BD2 /* ConnectableMapTests.swift in Sources */,
14221432
5B1F105121105CC50067193C /* EventSource+ExtensionsTests.swift in Sources */,
14231433
5BCF5F0420F3636800721C0D /* ConnectableClassTests.swift in Sources */,
14241434
2DA1E8A12449FBC500D240B7 /* WikiTutorialTest.swift in Sources */,

MobiusExtras/Source/ConnectableContramap.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,18 @@
2020
import MobiusCore
2121

2222
public extension Connectable {
23-
func contramap<NewInput>(_ map: @escaping (NewInput) -> Input) -> AnyConnectable<NewInput, Output> {
24-
let newConnectClosure = { (consumer: @escaping Consumer<Output>) -> Connection<NewInput> in
25-
let connection = self.connect(consumer)
26-
let mappedAcceptFunction = { (newTypeInput: NewInput) in
27-
let oldTypeInput = map(newTypeInput)
28-
connection.accept(oldTypeInput)
29-
}
23+
/// Transform the input type of this `Connectable` by applying the `transform` function to each input.
24+
///
25+
/// - Parameter transform: The function which should be used to transform the input to this `Connectable`
26+
/// - Returns: A `Connectable` which applies `transform` to each input value before handling it.
27+
func contramap<NewInput>(_ transform: @escaping (NewInput) -> Input) -> AnyConnectable<NewInput, Output> {
28+
return AnyConnectable { dispatch in
29+
let connection = self.connect(dispatch)
3030

31-
return Connection<NewInput>(
32-
acceptClosure: mappedAcceptFunction,
31+
return Connection(
32+
acceptClosure: { connection.accept(transform($0)) },
3333
disposeClosure: connection.dispose
3434
)
3535
}
36-
37-
let contramapped = AnyConnectable(newConnectClosure)
38-
39-
return contramapped
4036
}
4137
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) 2020 Spotify AB.
2+
//
3+
// Licensed to the Apache Software Foundation (ASF) under one
4+
// or more contributor license agreements. See the NOTICE file
5+
// distributed with this work for additional information
6+
// regarding copyright ownership. The ASF licenses this file
7+
// to you under the Apache License, Version 2.0 (the
8+
// "License"); you may not use this file except in compliance
9+
// with the License. You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing,
14+
// software distributed under the License is distributed on an
15+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
// KIND, either express or implied. See the License for the
17+
// specific language governing permissions and limitations
18+
// under the License.
19+
20+
import MobiusCore
21+
22+
public extension Connectable {
23+
/// Transform the output type of this `Connectable` by applying the `transform` function to each output.
24+
///
25+
/// - Parameter transform: The function which should be used to transform the output of this `Connectable`
26+
/// - Returns: A `Connectable` which applies `transform` to each output value.
27+
func map<NewOutput>(_ transform: @escaping (Output) -> NewOutput) -> AnyConnectable<Input, NewOutput> {
28+
return AnyConnectable { dispatch in
29+
return self.connect { output in
30+
let newOutput = transform(output)
31+
dispatch(newOutput)
32+
}
33+
}
34+
}
35+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) 2020 Spotify AB.
2+
//
3+
// Licensed to the Apache Software Foundation (ASF) under one
4+
// or more contributor license agreements. See the NOTICE file
5+
// distributed with this work for additional information
6+
// regarding copyright ownership. The ASF licenses this file
7+
// to you under the Apache License, Version 2.0 (the
8+
// "License"); you may not use this file except in compliance
9+
// with the License. You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing,
14+
// software distributed under the License is distributed on an
15+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
// KIND, either express or implied. See the License for the
17+
// specific language governing permissions and limitations
18+
// under the License.
19+
20+
import MobiusCore
21+
import MobiusExtras
22+
import Nimble
23+
import Quick
24+
25+
class ConnectableMapTests: QuickSpec {
26+
override func spec() {
27+
context("Connectable Map") {
28+
it("applies the `transform` function to the output") {
29+
var output: [Int?] = []
30+
let connection = TestConnectable()
31+
.map { Int($0) }
32+
.connect {
33+
output.append($0)
34+
}
35+
36+
connection.accept("1")
37+
connection.accept("2")
38+
connection.accept("3")
39+
40+
expect(output).to(equal([1, 2, 3]))
41+
42+
connection.dispose()
43+
}
44+
45+
it("preserves the connectable's `Disposable` conformance") {
46+
let testConnectable = TestConnectable()
47+
expect(testConnectable.isDisposed).to(beFalse())
48+
49+
testConnectable
50+
.connect { _ in }
51+
.dispose()
52+
53+
expect(testConnectable.isDisposed).to(beTrue())
54+
}
55+
}
56+
}
57+
}
58+
59+
private final class TestConnectable: Connectable {
60+
var isDisposed = false
61+
62+
func connect(_ consumer: @escaping Consumer<String>) -> Connection<String> {
63+
return Connection(
64+
acceptClosure: consumer,
65+
disposeClosure: { self.isDisposed = true }
66+
)
67+
}
68+
}

0 commit comments

Comments
 (0)