[Swift Combine Basics] Subject ๐Ÿ™‹โ€โ™€๏ธ๐Ÿ™‹โ€โ™‚๏ธ

devapploperยท2020๋…„ 12์›” 1์ผ
4
post-thumbnail

Subject๋Š” Subscriber์ด๋ฉฐ ๋™์‹œ์— Publisher์ด๋ฉฐ Value์ด๋‹ค.

A subject can receive values from an upstream publisher, and also can pass along these values to its downstream subscribers.

: Subject๋Š” ์—…์ŠคํŠธ๋ฆผ ๋ฐœํ–‰์ž๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜๋„ ์žˆ๊ณ , ๋‹ค์šด์ŠคํŠธ๋ฆผ ๊ตฌ๋…์ž์—๊ฒŒ ๊ฐ’์„ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๋˜ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค.

dafuq? ๐Ÿคจ

Subjects ์—๋Š” 2๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋Š”๋ฐ, ์šฐ์„  ๊ทธ ์ค‘ ํ•˜๋‚˜์ธ PassThroughSubject๋กœ ๋งŒ๋“  ์˜ˆ์‹œ๋ฅผ ํ•œ๋ฒˆ ๋ณด์ž.

์•„๋ž˜๋Š” PassThroughSubject ๊ฐ€ ๊ตฌ๋…์ž ์—ญํ• ์„ ํ•˜๋Š” ๋ชจ์Šต์ด๋‹ค.

// ๋ฐœํ–‰์ž
let publisher = [1, 2, 3].publisher

// ๊ตฌ๋…์ž ์—ญํ• ์„ ํ•  passthroughsubject
let passThroughSubject = PassThroughSubject<Int, Never>()

// ๋‘˜์„ ์—ฎ์œผ๋ ค๋ฉด - AnySubscriber(s: Subject)๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 
// (PassThroughSubject๋Š” Subject ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•œ๋‹ค.)
let anySubscriber = AnySubscriber(passThroughSubject)

// PassThroguhSubject๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ตฌ๋…๊ด€๊ณ„๊ฐ€ ์„ฑ๋ฆฝ๋œ ๋ชจ์Šต (Subject์˜ ๊ตฌ๋…์ž ์—ญํ• )
publisher.receive(subscriber: anySubscriber)

์ด๋ฒˆ์—๋Š” ๋ฐœํ–‰์ž ์—ญํ• ์„ ํ•˜๋Š” ๋ชจ์Šต์ด๋‹ค.

// ๋ฐœํ–‰์ž ์—ญํ• ์„ ํ•  PassThroughSubject
let passThorughSubject = PassThroughSubject<Int, Never>()

// sink() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น passThroughSubject์™€ ์—ฐ๊ฒฐ๋˜์–ด์žˆ๋Š” ๋‹ค์šด์ŠคํŠธ๋ฆผ ๊ตฌ๋…์ž๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
// ๊ฐ’์„ ๊ตฌ๋…์ž์—๊ฒŒ ๋‚ด๋ ค๋‹ค์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์„œ๋Š” ๋ฐœํ–‰์ž ์—ญํ• ์„ ํ•œ๋‹ค.
let newSubscriber = passthroughSubject
            .sink { value in
              print(value)
            }

์ด๋Ÿฐ์‹์œผ๋กœ subject๋Š” ๋‘๊ฐ€์ง€ ์—ญํ• ์„ ๋‹ค ํ•  ์ˆ˜ ์žˆ๋Š” SOLID ์›์น™์— ์œ„๋ฐฐ๋˜๋Š” ๊ณ ๋งˆ์šด ์นœ๊ตฌ.

์œ„์—์„œ Subject์—๋Š” ๋‘๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์ด์ œ๋Š” ์ด ๋‘๊ฐ€์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

์ฒซ๋ฒˆ์งธ๋กœ ์ข€์ „์— ์˜ˆ์‹œ์—์„œ ๋งŒ๋‚ฌ๋˜ PassThroughSubject , ๋‘๋ฒˆ์งธ๋กœ CurrentValueSubject ๊ฐ€ ์žˆ๋‹ค.

๋‘˜๋‹ค ๊ณตํ†ต์ ์œผ๋กœ Subject ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜์—ฌ ์•ž์„œ ๋ณด์•˜๋“ฏ์ด ๋ฐœํ–‰์ž ๋ฐ ๊ตฌ๋…์ž ์—ญํ• ์„ ์†Œํ™”ํ•ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

๋ฐœํ–‰์ž ์—ญํ• ์˜ ๊ด€์ ์—์„œ ๋‘ subject์˜ ๋‹ค๋ฅธ ์ ์ด ์žˆ๋‹ค๋ฉด CurrentValueSubject ๋Š” ์ดˆ๊ธฐ๊ฐ’์ด ์žˆ๊ณ , PassThroughSubject ๋Š” ์ดˆ๊ธฐ๊ฐ’์ด ์—†๊ณ  ๋ฐœํ–‰์„ ์‹œ์ž‘ํ•œ ์‹œ์ ๋ถ€ํ„ฐ ๊ฐ’์ด ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

CurrentValueSubject ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์„ฑ์‹œ์— ์ดˆ๊ธฐ๊ฐ’์„ ๋‹ด์•„์•ผํ•œ๋‹ค.

let currentSubject = CurrentValueSubject<Int, Never>(99)

PassThroughSubject ๋Š” ์ƒ์„ฑ์‹œ์—๋Š” ์ดˆ๊ธฐ๊ฐ’์„ ๋‹ด์„ ์ˆ˜ ์—†๋‹ค.

let passthroughSubject = PassthroughSubject<Int, Never>()

์ด๋Ÿฌํ•œ ๋‘˜์˜ ์ฐจ์ด๋•Œ๋ฌธ์—, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฐจ์ด๊ฐ€ ํŒŒ์ƒ๋œ๋‹ค.

"CurrentValueSubject ๋Š” ๊ตฌ๋…์ด ์‹œ์ž‘๋˜๋Š” ์‹œ์ ์— ์ดˆ๊ธฐ๊ฐ’์ด ๊ฒŒ์‹œ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ๊ฒฐ์ด ๋˜์ž๋งˆ์ž ๊ฐ’์ด ๊ตฌ๋…์ž์—๊ฒŒ ์ „๋‹ฌ๋˜์ง€๋งŒ, PassThroughSubject๋Š” ๊ตฌ๋…์ด ์‹œ์ž‘๋˜๊ณ , ๊ทธ ํ›„์— ๊ฐ’์ด ๊ฒŒ์‹œ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ ๊ตฌ๋…์ž์—๊ฒŒ ๊ฐ’์ด ์ „๋‹ฌ๋œ๋‹ค."

์ด๊ฒŒ ๋ฌด์Šจ๋ง์ธ์ง€๋Š” ์•„๋ž˜์˜ ์˜ˆ์‹œ๋ฅผ ๋ณด๋ฉด ํ•œ๋ฒˆ์— ์ดํ•ด๊ฐ€ ๊ฐˆ ๊ฒƒ์ด๋‹ค. ์šฐ์„ ์€ CurrentValueSubject ์˜ ๋™์ž‘์„ ์‚ดํŽด๋ณด์ž

// message ๋ฌธ์ž์—ด ๋ณ€์ˆ˜๊ฐ’์„ ๋‹ด์€ currentValueSubject๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ message๋Š” ์ดˆ๊ธฐ๊ฐ’์ด๋‹ค.
let message = "๋„ทํ”Œ๋ฆญ์Šค ๊ตฌ๋…์ด ์‹œ์ž‘๋˜์—ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ฅผ ๋ฐ”๋กœ ์ด์šฉํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค."
let currentValueSubject = CurrentValueSubject<String, Never>(message)

// sink()๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ตฌ๋…์ž๋ฅผ ๋ฐ”๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค. 
// ๊ตฌ๋…์ž์™€ ์—ฐ๊ฒฐ์ด ๋œ ์งํ›„ ๋ฐœํ–‰์ž๋Š” ๊ฐ’์„ ๊ฒŒ์‹œํ•˜๋ฏ€๋กœ, currentValueSubject๋Š” ์ดˆ๊ธฐ๊ฐ’์ธ message	๊ฐ’์„ ๋‹ค์šด์ŠคํŠธ๋ฆผ ๊ตฌ๋…์ž์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.
// ๋‹ค์šด์ŠคํŠธ๋ฆผ ๊ตฌ๋…์ž๋Š” ํ•ด๋‹น ๊ฐ’์„ ๋ฐ›์•„์„œ print ํ•œ๋‹ค.
let subscriber = currentValueSubject
		.sink { message
                   print(message)
	         }

// ๋‘๋ฒˆ์งธ ๊ฐ’์„ ๊ฒŒ์‹œํ•œ๋‹ค. - ๊ตฌ๋…์ž๋Š” ๋‹ค์‹œํ•œ๋ฒˆ ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์„ print ํ•œ๋‹ค
currentValueSubject.send("์ƒˆ๋กœ์šด ์˜ํ™” ๋ฐ ๋“œ๋ผ๋งˆ๊ฐ€ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋ฐ”๋กœ ํ™•์ธํ•ด๋ณด์„ธ์š”!")


// - Output -
// "๋„ทํ”Œ๋ฆญ์Šค ๊ตฌ๋…์ด ์‹œ์ž‘๋˜์—ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ฅผ ๋ฐ”๋กœ ์ด์šฉํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค."
// "์ƒˆ๋กœ์šด ์˜ํ™” ๋ฐ ๋“œ๋ผ๋งˆ๊ฐ€ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋ฐ”๋กœ ํ™•์ธํ•ด๋ณด์„ธ์š”!"

CurrentValueSubject ๋Š” ์ดˆ๊ธฐ๊ฐ’์„ ๊ฐ€์ง€๊ณ , ๊ตฌ๋…์ด ์‹œ์ž‘๋˜๋Š” ์ˆœ๊ฐ„ ๊ฐ’์„ ๋‹ค์šด์ŠคํŠธ๋ฆผ ๊ตฌ๋…์ž์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.

์œ„์™€ ๊ฐ™์€ ๋™์ž‘์„ PassThroughSubject๋กœ ๋™์ผํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

์•ž์„œ ๋งํ–ˆ๋“ฏ์ด, PassThroughSubject ๋Š” CurrentValueSubject ์™€ ๋‹ค๋ฅด๊ฒŒ ์ดˆ๊ธฐ๊ฐ’์ด ์—†๋‹ค.

๊ทธ๋Ÿฌ๋ฏ€๋กœ ๊ตฌ๋…์ด ์‹œ์ž‘๋œ ์‹œ์ ์ด ์•„๋‹Œ PassThroughSubject ๊ฐ€ ๊ฐ’์„ ๊ฒŒ์‹œํ•œ ์ˆœ๊ฐ„๋ถ€ํ„ฐ ๊ฐ’์ด ์ „๋‹ฌ๋œ๋‹ค.

์ด๋ฒˆ์—๋„ ์—ญ์‹œ ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ๋‹จ๋ฒˆ์— ๋  ๊ฒƒ์ด๋‹ค.

let passThroughSubject = PassThroughSubject<String, Never>()

// sink()๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ตฌ๋…์ž๋ฅผ ๋ฐ”๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค. 
// ๊ตฌ๋…์ž์™€ ์—ฐ๊ฒฐ์ด ๋œ ์งํ›„ ๋ฐœํ–‰์ž๋Š” ๊ฐ’์„ ๊ฒŒ์‹œํ•˜์ง€๋งŒ, PassThroughSubject๋Š” ์ดˆ๊ธฐ๊ฐ’์ด ์—†๊ธฐ๋•Œ๋ฌธ์— ๋‹ค์šด์ŠคํŠธ๋ฆผ ๊ตฌ๋…์ž์—๊ฒŒ ์ „๋‹ฌ๋˜๋Š” ๊ฐ’์ด ์—†๋‹ค.
let subscriber = passThroughSubject
                 .sink { message
		           print(message)
		 }


let messageA = "๋„ทํ”Œ๋ฆญ์Šค ๊ตฌ๋…์ด ์‹œ์ž‘๋˜์—ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ฅผ ๋ฐ”๋กœ ์ด์šฉํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค."
let messageB = "์ƒˆ๋กœ์šด ์˜ํ™” ๋ฐ ๋“œ๋ผ๋งˆ๊ฐ€ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋ฐ”๋กœ ํ™•์ธํ•ด๋ณด์„ธ์š”!"

// send()๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ๊ฒŒ์‹œํ•˜์—ฌ ๊ฐ’์„ ๊ตฌ๋…์ž์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.
passThroughSubject.send(messageA)
passThroughSubject.send(messageB)


// - Output -
// "๋„ทํ”Œ๋ฆญ์Šค ๊ตฌ๋…์ด ์‹œ์ž‘๋˜์—ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค. ์„œ๋น„์Šค๋ฅผ ๋ฐ”๋กœ ์ด์šฉํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค."
// "์ƒˆ๋กœ์šด ์˜ํ™” ๋ฐ ๋“œ๋ผ๋งˆ๊ฐ€ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋ฐ”๋กœ ํ™•์ธํ•ด๋ณด์„ธ์š”!"

Completion Event - ์„œ๋น„์Šค ์ข…๋ฃŒ ๐Ÿ”š

Subject๋Š” completion ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์œผ๋ฉด ๋”์ด์ƒ ๊ฐ’์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.

์ด๋Š” ๋งˆ์น˜ ์›”๊ฐ„ ๊ตฌ๋… ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•˜๋Š” ๊ตฌ๋…์ž์—๊ฒŒ ์„œ๋น„์Šค๊ฐ€ ๋๋‚ฌ์Œ์„ ์•Œ๋ฆฌ๊ณ , ๊ตฌ๋… ์„œ๋น„์Šค๋Š” ์ƒˆ๋กœ์šด ์›”๊ฐ„ ๊ตฌ๋… ์ƒํ’ˆ์„ ์ถœ์‹œํ•œ๋‹ค. ์„œ๋น„์Šค๊ฐ€ ์ข…๋ฃŒ๋œ ๊ตฌ๋…์ž๋Š” ๋”์ด์ƒ ํ•ด๋‹น ์„œ๋น„์Šค์˜ ๊ตฌ๋… ์ƒํ’ˆ์„ ๋ฐ›์•„๋ณผ ์ˆ˜ ์—†๊ณ , ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐ›์•„๋ณธ ์ƒํ’ˆ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ ์œ ์‚ฌํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

let currentValueSubject = CurrentValueSubject<Int, Never>(1)
print(currentValueSubject.value)
// 1

currentValueSubject.send(2)
print(currentValueSubject.value)
// 2

currentValueSubject.send(completion: .finished)

currentValueSubject.send(10)

print(currentValueSubject.value)
// 10์ด ์•„๋‹Œ 2๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค 

์žฅ๋‹จ์  โž•โž–

์žฅ์  :

  • @Published ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ Protocol ์„ ์–ธ์— ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. (Property wrapper๋Š” ํ”„๋กœํ† ์ฝœ ์•ˆ์—์„œ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค)

  • @Published ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ error type ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๋‹จ์ :

  • ์ฝ”๋“œ๊ฐ€ ์•ฝ๊ฐ„ ๊ธธ์–ด์ง„๋‹ค (?)

์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ์ถ”๊ฐ€์ ์ธ ์กฐ์‚ฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค ๐Ÿงช

profile
app ์„ dev ํ•˜๋Š” developer. devapploper ์ž…๋‹ˆ๋‹ค

0๊ฐœ์˜ ๋Œ“๊ธ€