Combine: шта је то, кључни појмови и Publisher са Subscriber

Аутор: IT Sectr Објављено: 2026-03-17 Време читања: 8 мин

Combine је оквир реактивног програмирања од Apple-а, представљен у iOS 13. Према Apple Documentation, 2025, Combine пружа јединствени декларативни API за обраду асинхроних догађаја. Publisher одређује извор података, а Subscriber се претплаћује на њихово примање.

Главно

  • Combine — уграђени Apple оквир за реактивно програмирање, доступан од iOS 13
  • Publisher — протокол који одређује извор вредности Output са могућношћу грешке Failure
  • Subscriber — протокол који прима вредности од Publisher-а са управљаним demand-ом
  • Subject — Publisher који дозвољава ручно уметање догађаја (PassthroughSubject, CurrentValueSubject)
  • Operators — map, filter, combineLatest, flatMap за трансформацију и комбиновање токова

Шта је Combine?

Combine је декларативни Swift оквир за обраду асинхроних догађаја током времена. За разлику од приступа заснованих на callback-у, Combine омогућава описивање цевовода обраде података кроз ланац оператора. Оквир је интегрисан са Foundation (URLSession, Timer, NotificationCenter) и SwiftUI (ObservableObject, @Published).

Протокол Publisher

Сваки тип који имплементира протокол Publisher мора дефинисати Output (тип вредности) и Failure (тип грешке). Publisher није активан до претплате — почиње да емитује догађаје тек након позива subscribe. Ово је хладна семантика, карактеристична за реактивне токове, која повећава ефикасност.

Протокол Subscriber

Subscriber прима догађаје кроз три метода: receive(subscription:) — потврда претплате са управљањем demand-ом; receive(_:) — примање нове вредности; receive(completion:) — завршетак тока са успехом или грешком. Demand одређује број вредности које је Subscriber спреман да прими — ово је механизам backpressure-а.

Уграђени Publisher-и

Apple пружа бројне уграђене Publisher-е: Just емитује једну вредност и завршава, Future — асинхрони резултат са затварањем, Deferred — одложено креирање Publisher-а до тренутка претплате. URLSession.dataTaskPublisher претвара мрежни захтев у реактивни ток. Timer.publish креира периодични тајмер. NotificationCenter.default.publisher претвара обавештења у Publisher. Ове интеграције омогућавају претплату на догађаје без ручног моста.

Publisher и Subscriber у Combine-у

У Combine-у Publisher и Subscriber су повезани протоколом Subscription. Subscription је објекат за повезивање који контролише ток података. Програмер не треба да креира сопствене типове Publisher-а — Apple пружа уграђене: Just, Future, Deferred, Fail, Empty, као и Publisher за URLSession и Timer.

Subject у Combine-у

Subject је Publisher у који се могу ручно слати вредности. PassthroughSubject не чува стање: претплатник прима само догађаје који су се десили након претплате. CurrentValueSubject, аналоган BehaviorRelay-у у RxSwift-у, чува тренутну вредност и одмах је прослеђује новом претплатнику.

Backpressure

Механизам backpressure у Combine-у регулише брзину преноса података. Subscriber обавештава Publisher преко subscription.request(.unlimited) или subscription.request(.max(N)) колико вредности је спреман да обради. Ово спречава препуњавање бафера при неједнакој брзини произвођача и потрошача.

Оператори Combine-а

Combine укључује преко 100 оператора подељених у категорије: трансформација (map, tryMap, flatMap), филтрирање (filter, compactMap, removeDuplicates), комбиновање (combineLatest, merge, zip), управљање временом (debounce, throttle, delay) и обрада грешака (catch, replaceError, retry).

Оператор combineLatest комбинује последње вредности два Publisher-а — сваки пут када било који од њих емитује нову вредност, затварање се позива са обе ажуриране вредности. Овај оператор је незаменљив при валидацији форми где је потребно пратити стање више поља за унос истовремено.

Оператор debounce одлаже објављивање вредности док не прође одређени интервал без нових догађаја. Ово је критично за поља за претрагу: захтев ка серверу се шаље тек након што корисник престане да куца на 300-500 ms, што смањује оптерећење 5-10 пута.

Обрада грешака

Combine нуди неколико стратегија обраде грешака на нивоу оператора. catch пресреће Failure и замењује Publisher резервним — ток се наставља без пада. replaceError(with:) поставља подразумевану вредност уместо грешке. retry(_:) понавља претплату одређени број пута при неуспеху. Важно: тип грешке Never у потпису Publisher-а гарантује да се ток никада неће завршити грешком — ово омогућава коришћење Publisher-а у SwiftUI-ју без обраде Failure-а.

Примери кода Combine

Први пример показује основни рад са Just — Publisher-ом који емитује једну вредност и завршава:

swift
let publisher = Just("Hello Combine")

publisher
    .sink(receiveCompletion: { completion in
        print("Завршено: \(completion)")
    }, receiveValue: { value in
        print("Примљено: \(value)")
    })

Други пример демонстрира интеграцију Combine-а са URLSession за мрежни захтев са обрадом резултата на главној нити:

swift
let url = URL(string: "https://api.example.com")!

URLSession.shared
    .dataTaskPublisher(for: url)
    .map { $0.data }
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: { completion in
        if case .failure(let error) = completion {
            print("Грешка: \(error)")
        }
    }, receiveValue: { data in
        print("Података примљено: \(data.count)")
    })

Трећи пример — коришћење CurrentValueSubject за управљање стањем:

swift
let counter = CurrentValueSubject<Int, Never>(0)

counter
    .sink { value in
        print("Бројач: \(value)")
    }

counter.send(1) // Приказаће: Бројач: 1
counter.send(2) // Приказаће: Бројач: 2
print(counter.value) // Приказаће: 2

Combine vs RxSwift

Combine и RxSwift решавају исти задатак — реактивно програмирање, али са различитим приступима. Combine је изворни Apple оквир уграђен у систем: не захтева инсталацију зависности, интегрисан је са SwiftUI и Foundation. RxSwift је библиотека треће стране са подршком за iOS 9+ и ширим скупом оператора.

Перформансе Combine-а су веће у типичним сценаријима захваљујући изворној имплементацији у C-у. RxSwift пружа више алатки за отклањање грешака (RxSwift.Resources.total, Debug), али његова објектно-оријентисана архитектура може бити мање ефикасна на великим токовима података. Избор између њих је компромис између могућности и интеграције.

Важна разлика — модел backpressure-а. У Combine-у Subscriber управља demand-ом преко Subscription-а, што даје експлицитну контролу над брзином тока. У RxSwift-у backpressure се подразумевано не контролише — Observable емитује све вредности, а Subscriber их обрађује како стижу. За велике токове података Combine се показује предвидљивијим и сигурнијим по меморију.

Combine је дубоко интегрисан са SwiftUI на нивоу језика. @Published генерише Publisher, ObservableObject пружа objectWillChange Publisher. SwiftUI View се аутоматски претплаћује на ове Publisher-е и прецртава се при променама. За UIKit постоје спољни омотачи попут CombineCocoa, али Combine је првобитно дизајниран за SwiftUI, па његова интеграција у UIKit захтева додатни код. RxSwift, насупрот томе, има богат екосистем за UIKit: RxCocoa пружа реактивне омотаче за све UI елементе.

Combine подржава интеграцију са Core Data преко NSFetchedResultsController Publisher-а. Publisher емитује нове податке при свакој промени Core Data стека: додавање, брисање, ажурирање записа. Ово омогућава изградњу реактивних листа без ручног позивања reloadData. Комбинујући Combine са NotificationCenter Publisher-ом за UIApplication.willResignActiveNotification, програмер ствара реактивни слој за цео животни циклус апликације без делегата и callback-ова. За ObservableObject класе, SwiftUI аутоматски ажурира View при промени @Published својстава — Combine обезбеђује целу везу без иједне линије ручног кода. Модуларно тестирање Combine кода се изводи кроз XCTestExpectation и Publisher.sink у тест случајевима са изолацијом Scheduler-а преко ImmediateScheduler-а.

При раду са Combine-ом у UIKit пројектима, програмери често користе претплату кроз sink и чувају AnyCancellable у Set-у. Типичан образац: ViewController креира приказ, чува cancellables, претплаћује се на Publisher из ViewModel-а. При deinit-у све претплате се аутоматски отказују кроз Cancellables колекцију. За правилан рад са UIControl-ом у Combine-у може се користити UIControl.Event Publisher кроз publisher(for:) проширење.

Отклањање грешака Combine-а се врши кроз оператор print() или handleEvents(). print(String) бележи све догађаје: receive subscription, request demand, receive value, receive completion. handleEvents даје финију контролу: могу се одредити затварања за сваку фазу животног циклуса Publisher-а. За тестирање Combine-а постоји CombineExpectations — библиотека која омогућава проверу да Publisher емитује очекиване вредности у тестовима са виртуелним временом и контролом demand-а.

Combine у пракси

Combine се активно користи у SwiftUI-ју за управљање формама: свако @Published својство генерише Publisher, SwiftUI се претплаћује на њега кроз View.body. Комбинујући више Publisher-а кроз combineLatest, валидација форме постаје декларативна: сваки Publisher прати једно поље, combineLatest прикупља све вредности, map их проверава и враћа статус Submit дугмета. Publishers.Merge обједињује више Publisher-а истог типа у један — корисно за прикупљање догађаја са различитих контрола у јединствени ток.

За интеграцију Combine-а са UIKit-ом користите UIControl Publisher кроз проширење: button.publisher(for: .touchUpInside) враћа Publisher који емитује догађај при притиску. CombineCocoa — библиотека из заједнице која пружа Publisher за све UIControl догађаје, UITextView.text, UIScrollView.contentOffset. При одјави претплате, cancellables Store се чисти у deinit-у, гарантујући да ниједна претплата неће надживети свог власника. За UIControl са више стања користите Publishers.MergeMany за обједињавање Publisher-а у низ.

Често постављана питања

Од које верзије iOS-а је доступан Combine?

Combine је доступан од iOS 13, macOS 10.15, tvOS 13 и watchOS 6. Ово ограничава његову употребу у пројектима који захтевају подршку за старије верзије. За iOS 12 и ниже примењује се RxSwift или библиотеке треће стране.

Чиме се разликује PassthroughSubject од CurrentValueSubject-а?

PassthroughSubject не чува стање и не понавља последњу вредност новим претплатницима — они добијају само будуће догађаје. CurrentValueSubject чува тренутну вредност и одмах је прослеђује новом претплатнику. CurrentValueSubject.value омогућава читање и писање тренутне вредности синхроно.

Како се Combine интегрише са SwiftUI-јем?

Combine лежи у основи SwiftUI-ја: @Published генерише Publisher, ObservableObject користи objectWillChange Publisher. SwiftUI View се аутоматски претплаћује на @ObservedObject и @StateObject кроз Combine цевовод, прецртавајући View при промени својстава.

Шта је backpressure у Combine-у?

Backpressure — механизам управљања брзином преноса података. Subscriber обавештава Subscription колико вредности је спреман да обради (demand). Publisher не може да емитује више него што је затражено. Ово је критично при раду са великим токовима података из мрежних socket-ова.

Како обрађивати грешке у Combine-у?

Оператор catch пресреће грешку и замењује Publisher fallback-ом. replaceError замењује грешку подразумеваном вредношћу. retry понавља претплату одређени број пута при грешци. Сви оператори треба да се поставе пре subscribe како би се искључио пад при Failure-у.

Резиме

  • Combine — уграђени Apple оквир за реактивно програмирање од iOS 13
  • Publisher — протокол са Output и Failure, почиње рад након претплате
  • Subscriber — прима вредности кроз receive са подршком за demand
  • Subject (PassthroughSubject, CurrentValueSubject) — Publisher са ручним уметањем догађаја
  • Оператори map, filter, combineLatest, debounce, catch — основа цевовода
  • Backpressure кроз demand спречава препуњавање бафера Subscriber-а
  • Бирајте Combine за нове пројекте од iOS 13, RxSwift — за подршку старијих верзија

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође