[SWIFT] I want to use Combine in UIKit as well.

Introduction

Now that iOS14 has been released, Swft itself has been updated and Swift UI has been standardized, I think there are many people who can't cut iOS12 and can't use Combine in practice.

But I want to try new technology. SwiftUI + Combine is a bit high hurdle, but first of all, I would like to try Combine alone with the familiar UIKit! !!

So, this time I wrote an article with the following contents.

I hope you find it useful. : bow:

environment

This article has been confirmed to work in the following operating environment.

What is Combine?

In a nutshell, it's Apple's genuine asynchronous framework`. I will quote the description of Apple Developer.

The Combine framework provides a declarative Swift API for processing values over time.

It means something like "The Combine framework provides a declarative API for processing values over time."

This is the main function of Combine, and it seems to be the nature of the asynchronous framework.

Know the characters in Combine

Before trying to use Combine, I will introduce the overall flow and the characters that you need to know to use Combine.

Character

It looks like this in the figure.

Untitled Diagram.png

In Rx and reactive programming, the above concept is often expressed by a marble diagram, Personally, it's easier to get the concept in the figure that shows the characters 1-1-1 as shown in the above figure.

Let's touch it for the time being

Now that you know the characters, let's touch them for the time being.

This is the login screen you often see. If you're designing with an MVVM, you need to bind the email address you receive from the TextField in your View to your ViewModel. Let's take the implementation at that time as an example to see the explanation and implementation example of Publisher, Operator, and Subscriber.

Publisher First is Publisher. https://developer.apple.com/documentation/combine/publisher

Publisher is a great guy who can publish values, so you can publish the email address you received in View.

First, let's create a Publisher that publishes a value when the text of the TextField changes.

import Combine

final class LoginViewController: UIViewController {

    @IBOutlet weak var mailAddressTextField: UITextField!

    private let viewModel = LoginViewModel()
    private var binding = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //When textDidChangeNotification is notified`mailAddressTextField`Issue the object.
        NotificationCenter.default
            .publisher(for: UITextField.textDidChangeNotification, object: mailAddressTextField)

    }
}

Don't forget to import. Now when the textField changes, an object called mailAddressTextField will be issued.

Untitled Diagram (1).png

Operator Next, let's go to Operator. The Operator is a great guy who can convert values, so you can also convert the UITextField object published by Publisher above. When binding to a ViewModel with a TextField value, just a String is enough to pass to the ViewModel. So let's use Operator to convert the UITextField to a String.

import Combine

final class LoginViewController: UIViewController {

    @IBOutlet weak var mailAddressTextField: UITextField!

    private let viewModel = LoginViewModel()
    private var binding = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default
            .publisher(for: UITextField.textDidChangeNotification, object: mailAddressTextField)
            .compactMap { $0.object as? UITextField } //If you fail to cast UITextField and it becomes nil, play it
            .map { $0.text ?? "" } //Extract text from UITextField and convert to String
    }
}

I am doing that.

Untitled Diagram (2).png

In this way, when the value issued by Publisher and the value you want to receive are different, the Operator handles the conversion and handling of the value in the middle. See below for Operator types.

Subscriber The last is Sbscriber. Since Sbscriber is a great guy who can receive values and do various things, you can set the String of the email address sent from Publisher to the ViewModel.

import Combine

final class LoginViewController: UIViewController {

    @IBOutlet weak var mailAddressTextField: UITextField!
    
    private let viewModel = LoginViewModel()
    private var binding = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default
            .publisher(for: UITextField.textDidChangeNotification, object: mailAddressTextField)
            .compactMap { $0.object as? UITextField }
            .map { $0.text ?? "" }
            .removeDuplicates() //Eliminate duplicate values
            .eraseToAnyPublisher()
            .receive(on: RunLoop.main)
            .assign(to: \.mailAddress, on: viewModel) //viewModel\.Assign a value to mailAddress
            .store(in: &binding)
    }
}

final class LoginViewModel {
    var mailAddress: String = "" {
        didSet {
            print(mailAddress)
        }
    }
}

It looks like you're doing a lot, but there are two points.

The assign method grows from Publisher and can generate a Subscriber while passing the received value to the object defined by keypath. However, if it is left as it is, the memory will not be released, so we are calling .store (in: & binding) to cancel the monitoring.

Untitled Diagram (3).png

Run

With the code so far, when you receive the value of TextField, it will be set in ViewModel!

Summary

Combine is Apple's genuine asynchronous framework and has the following features.

Combine has the following major characters:

bonus

In order to get used to Combine, I am also making an application that manages books by hitting API with UIKit and Combine. I'm planning to include an API client that incorporates Combine and Publisher that has been made into an extension to make it easier to use, so please have a look if you like: bow:

kawano108/CombineBookManagerApp

Recommended Posts

I want to use Combine in UIKit as well.
I want to use FormObject well
I want to use @Autowired in Servlet
I want to use arrow notation in Ruby
I want to use Clojure's convenient functions in Kotlin
I want to use fish shell in Laradock too! !!
I want to use ES2015 in Java too! → (´ ・ ω ・ `)
I want to use a little icon in Rails
I want to use DBViewer with Eclipse 2018-12! !!
I want to get some properties as JSON strings in Jackson!
[Java Spring MVC] I want to use DI in my own class
I want to send an email in Java.
I want to use java8 forEach with index
I want to pass APP_HOME to logback in Gradle
rsync4j --I want to touch rsync in Java.
[Xcode] I want to manage images in folders
I want to be eventually even in kotlin
I want to get the value in Ruby
I want you to use Scala as Better Java for the time being
I want to do something like "cls" in Java
I want to use NetBeans on Mac → I can use it!
I want to embed any TraceId in the log
I want to define a function in Rails Console
[Android Studio] I want to use Maven library on Android
I want to stop snake case in table definition
I want to click a GoogleMap pin in RSpec
I want to use PowerMock in a class that combines parameterized tests and ordinary tests
I want to convert characters ...
[Beginner] I want to modify the migration file-How to use rollback-
I want to find a relative path in a situation using Path
I want to set the conditions to be displayed in collection_check_boxes
I want to use screen sharing on the login screen on Ubuntu 18
I want to perform high-speed prime factorization in Ruby (ABC177E)
[jackson] I want to receive JSON values "0" and "1" as boolean
I want to use the Java 8 DateTime API slowly (now)
I want to use the sanitize method other than View.
How to use ArgumentMatchers such as Mockito's any () in Kotlin
Even in Java, I want to output true with a == 1 && a == 2 && a == 3
I want to create a Parquet file even in Ruby
I want to transition to the same screen in the saved state
I want to use FireBase to display a timeline like Twitter
I want to simplify the conditional if-else statement in Java
Swift: I want to chain arrays
How to use Lombok in Spring
I want to use swipeback on a screen that uses XLPagerTabStrip
[Ruby] I want to put an array in a variable. I want to convert to an array
I tried setting Java beginners to use shortcut keys in eclipse
I want to convert InputStream to String
I want to docker-compose up Next.js!
How to use InjectorHolder in OpenAM
I want to display the images under assets/images in the production environment
I want to add devise in Rails, but I can't bundle install
How to use classes in Java?
I want to remove the top margin in Grouped UITableView (swift)
[Java] I want to perform distinct with the key in the object
I want to change the value of Attribute in Selenium of Ruby
[Android] I want to get the listener from the button in ListView
[Rails + Webpacker] I want to use images of assets! Until you can view the image in Vue.js
[Rails] I want to send data of different models in a form
I want to write JSP in Emacs more easily than the default.
I want to select multiple items with a custom layout in Dialog