SwiftUI

SwiftUI - refreshable

GREEN.1229 2022. 12. 8. 10:16

안녕하세요. 그린입니다🍏

이번 포스팅에서는 SwiftUI의 List에서 사용 가능한 refreshable이라는 새로 고침 기능에 대해 알아보겠습니다🙌

 

우선 해당 기능은 iOS 15 부터 사용이 가능해요!

참고로 WWDC2021에서 소개되었습니다.

비교적 어떻게 보면 최신 메서드이기에 현업에서 13, 14부터 지원을 한다면 사용할 순 없습니다.

그렇지만 앞으로 미니멈 타겟은 점차 올려갈것이고 배워두면 좋기에 이참에 한번 트라이 해봅니다🙌

 

여러분 모두 테이블뷰 혹은 컬렉션뷰로 된 즉, 리스트로 데이터가 뿌려진 뷰의 경우 최상단에서 아래로 드래깅 시 새로 고침이 되면서 데이터를 최신으로 갱신하는 그런 기능을 가진 앱을 아주 많이 보셨고 익숙하실거라 생각합니다.

모두에게 사실은 친숙한 기능이긴한데 SwiftUI에서 API로 제공하진 않았습니다.

그런데 15에서부터 아주 손쉽게 사용 가능하도록 제공이 되었죠😀

 

그럼 거두절미하고 바로 어떤 메서드인지 어떻게 사용하는지 보시죠!

 

refreshable?

SwiftUI에서 List의 뷰를 새로 고침 해줍니다.

설명 끄읕~~👏👏👏

한마디로 정의가 끝났습니다.

해당 메서드를 뷰 모디파이어로 붙이면 아래 드래깅하여 새로 고침을 할 수 있도록 제공해준다는거죠.

func refreshable(action: @escaping () async -> Void) -> some View

선언은 이렇습니다.

해당 뷰 모디파이어를 통해 async한 탈출 클로저를 실행시킵니다.

주로 최신의 데이터를 다시 fetch하는 작업이 이뤄지는게 맞을것 같아요.

사용자가 새로 고침을 요청할 때 발생하는 SwiftUI가 실행하는 비동기 처리기죠.

실행 코드 앞 await 키워드를 사용해 비동기 호출을 해주도록 합니다.

위와 같은 메서드를 사용하면 리스트에서 아래 드래깅을 통해 새로 고침을 해주지만 이를 커스텀한 뷰에 적용할 수 있습니다.

즉, 버튼을 눌러 새로 고침을 한다던지 등의 조금 더 커스텀한 구성이 RefreshAction 인스턴스 설정으로 가능하죠.

이건 또 아래에서 알아보겠습니다🙋🏻

 

그럼 우선 refreshable 메서드를 예시를 통해 사용해보겠습니다!

 

refreshable 사용하기

import SwiftUI

struct ContentView: View {
  @State var colors: [Color] = []
  
  var body: some View {
    VStack(spacing: 10) {
      Text("Colors")
        .font(.largeTitle)
        .padding(.top, 50)
      
      List(colors, id: \.self) { color in
        RoundedRectangle(cornerRadius: 10)
          .frame(width: 300, height: 50)
          .foregroundColor(color)
      }
      .onAppear {
        colors = initColors()
      }
      .refreshable {
        await colors.append(contentsOf: addColors())
      }
      
      Spacer()
    }
  }
  
  private func initColors() -> [Color] {
    return [.yellow, .green, .indigo, .orange, .purple]
  }
  
  private func addColors() async -> [Color] {
    return [.red, .blue, .gray]
  }
}

저는 별도 통신을 해주지 않고 야매로 데이터를 넣었습니다!

그렇기에 await, async를 사실 사용하지 않아도 무방하긴 해요ㅎㅎ

우선 List에 다양한 색상의 둥근 사각형을 초기에 5개 만들어주고 이후 refresh 될 때 3개를 추가해주는 로직입니다.

 

그럼 어떻게 나오는지 봐볼까요?

위와 같이 아래로 드래깅 시 새로 고침의 인디케이터가 돌고 정의한 색상이 추가되어 뷰가 업데이트 되는것을 확인할 수 있습니다.

아주 사용이 쉽습니다ㅎㅎ

추후에 실 데이터 통신의 역할을 addColors 메서드 대신 갈아끼워 데이터를 갱신해오는 로직을 가져가면 됩니다🙌

 

그럼 이어서 아까 짚고 넘어가자던 RefreshAction에 대해 알아보겠습니다!

 

RefreshAction?

위에서 간단히 언급했듯이 새로 고침을 시작할 수 있도록 해주는 작업입니다.

struct RefreshAction

구조체 타입이며, 기본적으로 refresh 환경 값에 해당 인스턴스가 포함되어 있어 기본 뷰의 Environment가 새로 고침을 제공해주죠.

디폴트하게 이 Environment의 값은 nil이지만 refreshable 메서드를 이용하였을때 지정한 처리를 하는 새로 고침 작업을 해줄 수 있습니다.

이제 RefreshAction을 이용해 커스텀한 뷰를 만들어 새로 고침을 해줄 수 있습니다.

refresh Environment 변수를 이용하면 됩니다🙌

아래 예시를 들어보겠습니다. (공식문서 참고한 예시!)

struct RefreshableView: View {
  @Environment(\.refresh) private var refresh

  var body: some View {
    Button("Refresh") {
      Task {
        await refresh?()
      }
    }
    .disabled(refresh == nil)
  }
}

refresh를 callAsFunction 이용해 직접 정의하고 호출하는 메서드로 사용할 수 있습니다.

여기서 callAsFunction은 새로 고침 작업을 시작하는 메서드입니다.

호출이 비동기적이므로 Task와 await를 이용합니다.

 

설명이 조금 복잡할 수 있는데 사용은 어렵지 않으니 바로 사용법을 보시죠🕺🏻

 

커스텀하게 새로 고침 기능 사용하기

import SwiftUI

struct ContentView: View {
  @State var colors: [Color] = []
  
  var body: some View {
    VStack(spacing: 10) {
      Text("Colors")
        .font(.largeTitle)
        .padding(.top, 50)
      
      List(colors, id: \.self) { color in
        RoundedRectangle(cornerRadius: 10)
          .frame(width: 300, height: 50)
          .foregroundColor(color)
      }
      .onAppear {
        colors = initColors()
      }
      
      CustomRefresher()
        .refreshable {
          await colors.append(contentsOf: addColors())
        }
      
      Spacer()
    }
  }
  
  private func initColors() -> [Color] {
    return [.yellow, .green, .indigo, .orange, .purple]
  }
  
  private func addColors() async -> [Color] {
    return [.red, .blue, .gray]
  }
}

struct CustomRefresher: View {
  @Environment(\.refresh) private var refresh
  
  var body: some View {
    Group {
      if let refresh = refresh {
        Button(
          action: {
            Task {
              await refresh()
            }
          },
          label: {
            Text("Refresh Button")
          }
        )
      }
    }
  }
}

CustomRefresher라는 뷰를 만들어줍니다.

refresh Environment 변수를 가지죠!

버튼의 action 동작으로 비동기 작업을 위해 Task에 refresh의 callAsFunction을 호출해주도록 합니다.

이 커스텀한 뷰를 원하는곳에 배치하고 위에서 알아본 refreshable 메서드를 통해 구현해주면 끝🙌

 

그럼 요렇게 버튼을 눌렀을때 데이터가 추가되고 뷰가 업데이트 됩니다👍

 

마무리

이렇게 오늘도 SwiftUI의 적당한 괜찮은 기능을 알아봤습니다!

해당 위 예시 코드들은 아래 제 깃헙에도 있으니 참고하실분들은 참고하셔도 좋겠습니다😊

https://github.com/GREENOVER/playground/tree/main/refreshable

 

GitHub - GREENOVER/playground: 학습을 하며 간단한 예제들을 만들어 보는 작고 소중한 놀이터

학습을 하며 간단한 예제들을 만들어 보는 작고 소중한 놀이터. Contribute to GREENOVER/playground development by creating an account on GitHub.

github.com

 

[참고자료]

https://developer.apple.com/documentation/swiftui/view/refreshable(action:) 

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/swiftui/refreshaction

 

Apple Developer Documentation

 

developer.apple.com