개발

Combine ObservableObject

한번사는인생~키야 2024. 7. 8. 14:11
728x90

Combine에서 SwiftUI 뷰 내에서 관찰 가능한 상태를 관리하는 것을 간소화하는 프로토콜입니다. 기본 객체의 상태가 변경될 때마다 뷰에 자동으로 알리는 메커니즘을 제공합니다.

다음은 세부 내용입니다.

  • 게시된 속성: 클래스 내의 속성을 로 표시하여 @Published해당 속성이 관찰 가능하고 값이 변경될 때 뷰 업데이트를 트리거할 수 있음을 나타냅니다.
  • ObjectWillChange Publisher: 속성이 변경 되기 전에 이벤트를 내보내는 게시자를 자동으로 합성합니다 . ObservableObjectobjectWillChange@Published
  • 이익:
  • SwiftUI 뷰 내에서 상태 관리가 간소화되었습니다.
    상태가 변경될 때 자동으로 뷰가 무효화되고 업데이트됩니다.
    코드 가독성과 유지관리성이 향상되었습니다.

사용:

  1. ObservableObject 클래스 생성: 프로토콜 을 준수하는 클래스를 정의합니다 ObservableObject.
  2. 속성을 @Published로 표시: 변경 사항을 관찰하려는 클래스 내의 속성을 선언하고 이를 @Published. 으로 표시합니다.
  3. SwiftUI 뷰에서 관찰된 값에 액세스: SwiftUI 뷰에서 인스턴스를 환경 객체로 주입 하고 속성에 액세스하여 상태를 표시하거나 상태와 상호 작용합니다.ObservableObject@Published

예: ObservableObject를 사용한 사용자 설정:

import SwiftUI

import Combine



class UserSettings: ObservableObject {

  @Published var username: String = ""

  @Published var isLoggedIn: Bool = false

}



struct ContentView: View {

  @EnvironmentObject var settings: UserSettings



  var body: some View {

    VStack {

      TextField("Username", text: $settings.username)

      Toggle("Logged In", isOn: $settings.isLoggedIn)

      Text("Username: \(settings.username)")

      Text("Logged In: \(settings.isLoggedIn ? "Yes" : "No")")

    }

  }

}

 

설명:

  1. 우리는 UserSettings에 맞는 클래스를 정의합니다 ObservableObject.
  2. 우리 는 두 가지 속성을 가지고 있습니다 @Published: . usernameisLoggedIn
  3. ContentView구조체 에서 인스턴스 에 접근하기 위해 를 선언합니다 .settings@EnvironmentObjectUserSettings
  4. 구문을 사용하여 TextField및 를 Toggle해당 속성에 바인딩합니다 .@Published$
  5. 우리는 객체의 현재 값 username과 객체 isLoggedIn를 기반으로 한 값을 표시합니다 UserSettings.

키 포인트:

  • ObservableObjectSwiftUI에서 상태 변경 사항을 뷰에 자동으로 알림으로써 상태 관리를 간소화합니다.
  • 뷰 업데이트를 트리거해야 하는 속성을 표시하는 데 사용합니다 .@Published
  • 환경 객체 주입과 바인딩을 사용하여 SwiftUI 뷰 내의 속성에 액세스합니다 .@Published

사용하면 ObservableObjectSwiftUI 애플리케이션에서 더욱 반응성 있고 대응성이 뛰어난 사용자 인터페이스를 만들 있습니다.

 

SwiftUI 애플리케이션에서 상태를 관리하고 뷰를 업데이트하는 데 Combine을 사용하는 방법에 대한 몇 가지 추가 예는 다음과 같습니다 

1. ObservableObject를 사용한 카운트다운 타이머:

import SwiftUI

import Combine



class CountdownTimer: ObservableObject {

  @Published var timeRemaining: Int = 10



  let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()



  init() {

    timer.subscribe(on: DispatchQueue.main) { [weak self] _ in

      guard let self = self else { return }

      if self.timeRemaining > 0 {

        self.timeRemaining -= 1

      } else {

        self.timer.upstreamSubscription.cancel()

      }

    }

  }

}



struct CountdownView: View {

  @ObservedObject var timer = CountdownTimer()



  var body: some View {

    VStack {

      Text("Countdown: \(timer.timeRemaining)")

        .font(.largeTitle)

        .fontWeight(.bold)



      if timer.timeRemaining == 0 {

        Text("Time's up!")

          .foregroundColor(.red)

      }

    }

  }

}

 

설명:

  1. 우리는 CountdownTimer에 맞는 클래스를 생성합니다 ObservableObject.
  2. 해당 @Published속성은 timeRemaining남은 카운트다운 시간을 보관합니다.
  3. 우리는 Timer1초마다 값을 게시하는 를 초기화합니다.
  4. 타이머는 자기 자신을 구독하여 subscribe(on:)메인 스레드에서 업데이트가 발생하도록 보장합니다.
  5. 구독 종료 내에서 타이머가 0에 도달하면 감소 하고 취소됩니다.timeRemaining
  6. 에서는 CountdownView. 을 사용하여 인스턴스를 주입합니다 .CountdownTimer@ObservedObject
  7. 값을 표시 하고 타이머가 0에 도달하면 timeRemaining"시간 초과!"라는 메시지를 표시합니다 .

 

2. 네트워크 요청에서 데이터 업데이트:

import SwiftUI

import Combine



class NetworkViewModel: ObservableObject {

  @Published var posts: [Post] = []



  private let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!

  private let cancellables = Set<AnyCancellable>()



  func loadPosts() {

    URLSession.shared.dataTaskPublisher(for: url)

      .map { $0.data }

      .decode(type: [Post].self, decoder: JSONDecoder())

      .receive(on: .main)

      .sink(receiveCompletion: { [weak self] completion in

        if case .failure(let error) = completion {

          print("Error fetching posts: \(error)")

        }

      }, receiveValue: { [weak self] posts in

        self?.posts = posts

      })

      .store(in: &self.cancellables)

  }

}



struct PostListView: View {

  @ObservedObject var viewModel = NetworkViewModel()



  var body: some View {

    List(viewModel.posts) { post in

      Text(post.title)

    }

    .onAppear {

      viewModel.loadPosts()

    }

  }

}

 

설명:

  1. 우리는 NetworkViewModel에 맞는 클래스를 정의합니다 ObservableObject.
  2. 해당 @Published속성은 posts네트워크에서 가져온 게시물을 보관합니다.
  3. 이 loadPosts방법은 URLSession을 사용하여 게시물을 가져오고 JSON 응답을 디코딩합니다.
  4. UI 업데이트를 위해 메인 스레드에서 receive(on: .main)업데이트가 발생하도록 보장 합니다 
  5. 에서는 PostListView. 을 사용하여 인스턴스를 주입합니다 .NetworkViewModel@ObservedObject
  6. 를 사용하여 게시물 목록을 표시 List하고 이를 viewModel.posts. 에 바인딩합니다.
  7. onAppear수정자 에서 뷰가 나타날 때 초기 데이터 가져오기를 트리거하기 위해 호출합니다 .viewModel.loadPosts()