Swift Combine First step

Overview

For those who are new to Swift and want to get a feel for Combine. Here's a simple code.

Today's example

The goal is to understand this today! You don't have to know. I will explain later. Those who understand do not have to read it.

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).map{b,a in a + b}

publisher.sink{ added in
    print(added)
}
a.send(10)

I don't think it really makes sense, but what is likely to be displayed on standard output (print)?

The answer is

4
11

Two lines are displayed. The first 4 is 3 + 1 The next 11 is 10 + 1.

Import for the time being

Combine import


import Combine

You can do it with. Available on ** iOS 13 ** and above.

You can easily try it on Playground.

Combine terminology

Publisher and Subscriber are the terms that should be remembered for the time being in Combine.

name Japanese reading what are you doing
Publisher Publisher Publish data
Subscriber Subscriber Subscribe to data

You don't have to understand it, so memorize 2 words for the time being! Publisher, Subscriber

Just for the time being

Just is (probably) the easiest Publisher.

Let's go see the definition for the time being. (Excerpt)

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct Just<Output> : Publisher {
   // ... (abridgement)
}

For the time being, it is a Publisher that inherits Publisher. It seems to be a fact that it can be used only on iOS 13.0 or higher.

What is written in this Just explanation is

A publisher that emits an output to each subscriber just once, and then finishes.

From this sentence you can see that:

Forcibly remember that it is a publisher that outputs one value.

About sink

With the example code


publisher.sink{ added in
    print(added)
}

There was.

Looking at the description of this [sink](https://developer.apple.com/documentation/combine/publisher/sink(receivevalue :)),

Attaches a subscriber with closure-based behavior to a publisher that never fails.

The meaning is unknown, but it seems that it will attach a subscriber, so let's use it.

Example 1

What happens if you call think with Just earlier?

Just(1).sink { r in
    print(r)
}

This will display 1.

Publisher (Just) outputs 1 only once, and it seems that 1 is displayed because it is entered in r by sinking.

Example 2

Let's also try CurrentValueSubject <Int, Never> (3), which we haven't discussed yet.

CurrentValueSubject<Int, Never>(3).sink { r in
    print(r)
}

This will output 3!

What's the difference with Just ...

About CurrentValueSubject

What is CurrentValueSubject? Let's look at the definition.


@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
final public class CurrentValueSubject<Output, Failure> : Subject

This guy seems to be ** Subject **. It wasn't Publisher! Also, it is the same as Just that it can be used only on iOS 13.0 or higher.

Description

A subject that wraps a single value and publishes a new element whenever the value changes.

Isn't it only Publisher that can publish? I will also go to see the definition of Subject.

public protocol Subject : AnyObject, Publisher

Apparently Subject is a ** Publisher type **.

Compared to Just

Just One outputHowever(just)Output oncePublisher
CurrentValueSubject Wrap one value to get a new value when the value changesPublishTo doSubject

The difference with Just is that when the value changes, it publishes the new value! Let's try it.


import Combine

let a = CurrentValueSubject<Int, Never>(3)

a.sink{ added in
    print(added)
}

a.value = 100
a.value = 10000
a.send(9999)

What is likely to be output? The answer is

3
100
10000
9999

It prints every time the a.value changes. It seems that a.send () can also express the change of value.

Last line!

Most of the first example has been explained.

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).map{b,a in a + b}

publisher.sink{ added in
    print(added)
}
a.send(10)

After that, if you know let publisher = b.combineLatest (a) .map {b, a in a + b}, you can understand the meaning of the code.

combineLatest

First, let's pay attention to b.combineLatest (a).

What does this mean?


let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

So both a and b are Publishers.

Let's take a look at the description of combineLatest.

Subscribes to an additional publisher and publishes a tuple upon receiving output from either publisher.

This guy seems to be both Subscribe and Publish.

Actually use

Try to move it.

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).sink{r in
    print(r)
}

What is going to happen with this?

The correct answer is

(1, 3)

Speaking of which, combineLatest said earlier that it would return tuples.

Let's change the value of a. Add 3 lines of code.

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).sink{r in
    print(r)
}

a.value = 4
a.value = 5
a.send(6)

What's going on?

The correct answer is

(1, 3)
(1, 4)
(1, 5)
(1, 6)

The value of b remains unchanged and is printed by changing the value of a.

Did you see the movement?

Who is combineLatest who subscribes and publishes?

Looking at the definition,


    public func combineLatest<P>(_ other: P) -> Publishers.CombineLatest<Self, P> where P : Publisher, Self.Failure == P.Failure

It's long and unclear, but it's returning Publishers.CombineLatest <Self, P>.

Publishers.CombineLatest


public struct CombineLatest<A, B> : Publisher

It's ** Publisher **.

So b.combineLatest (a) is a function that makes two Publishers one Publisher.

map

map is

Transforms all elements from the upstream publisher with a provided closure.

It seems that everything will be transformed.

Looking at the definition

public func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>

After all, this always returns Publisher!

Example 1

The example given earlier in combineLatest.

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).sink{r in
    print(r)
}

a.value = 4
a.value = 5
a.send(6)

Result is,

(1, 3)
(1, 4)
(1, 5)
(1, 6)

It was.

Try adding a map after combineLatest in this code.


import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a)
    .map{(b,a) in a+b}
    .sink{r in print(r)}

a.value = 4
a.value = 5
a.send(6)

What is likely to return?

The correct answer is

4
5
6
7

this is

(1, 3)
(1, 4)
(1, 5)
(1, 6)

You can see that each of these results adds two values.

Example 2

I changed .map {(b, a) in a + b} to .map {(b, a) in a}. What will the output be?


import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a)
    .map{(b,a) in a}
    .sink{r in print(r)}

a.value = 4
a.value = 5
a.send(6)

The correct answer is

3
4
5
6

Summary

To use Combine iOS 13.Above 0import Combine
What is Just Output the value only oncePublisher
What is a sink Subscribe to the value output by Publisher(Subscribe)Can be processed
CurrentValueSubject The value can be changed and the changed value is output.Publisher(Subject)
How to change the value of CurrentValueSubject? CurrentValueSubject.Assign to value or CurrentValueSubject.send(value)Enter a value in
What is combineLatest Makes two publishers one publisher and returns the value as a tuple.
What is map Transform all values from publisher

Did you understand the behavior of the code below the goal?

import Combine

let a = CurrentValueSubject<Int, Never>(3)
let b = Just(1)

let publisher = b.combineLatest(a).map{b,a in a + b}

publisher.sink{ added in
    print(added)
}
a.send(10)

Recommended Posts

Swift Combine First step
[Swift] First Firebase
[Swift] Type type-Class first part-
JAVA (First step: Git Bush edition)
HandleEvents closure not called in Swift Combine