iOS/SwiftUI

[SwiftUI] Non-constant range: not an integer range

Guryss 2024. 6. 12. 20:46

사이드 프로젝트 개발하는 도중, 서버에서 받아온 유저들의 랭킹 리스트들을 스크롤뷰에서 보일 수 있게 하는 과정에 Non-constant range: not an integer range 라는 경고문을 맞딱드렸다. 

homeViewModel의 totalRank를 RankingView에서 rankingList로 받아와 아래의 ForEach문에서 rankingList.indices 를 통해 인덱스로 접근하여 해당 배열을 순회하도록 했다.

해당 경고가 어떤 의미의 경고인지 알아보고, 어떻게 하면 경고를 없앨 수 있을지 알아보자!


 

ForEach(rankingList.indices)를 사용했을까?

이유는 단순했다. 해당 스크롤뷰의 각 row에는 유저의 현재 등수, 유저의 닉네임, 점수 총 3개를 보여줘야 했다.

이때 서버에선 1등부터 순서대로 유저 리스트를 보내주고 있으므로 해당 배열의 index+1을 등수로 넣어주면 되겠다고 생각했다. 

그렇다보니 index를 활용하게 되었고 index를 활용하기 위해선 indices를 활용하게 되었다. 

 

 

Non-constant range: not an integer range 란?

ForEach안에 들어간 배열의 범위가 constant하지 않다는 의미 즉, 상수 범위로 정의되어 있지 않다는 것이다. 

해당 배열 안에 Ranking이라는 객체가 전체 몇개가 있는지 상수로 정의되어 있지도 않고 만약에 해당 배열 안에서의 객체가 추가, 삭제가 발생한다면 범위가 dynamic 해져서 각 개체마다 id가 필요해지는 상황이 온다는 것이다.

 

그렇기 때문에 각 개체마다 id를 부여하여 해당 id값을 기준으로 변경사항이 이뤄질 수 있는 것이다. 

 

XCode상에서 ForEach를 선언해보면 id값이 없는 ForEach는 content가 Identifiable해야 한다는 점이 있다. 따라서 Int로 범위가 지정되어있지 않다면 반복문을 통해 순회하게 되는 대상은 식별가능한 개체들이 있어야 하는 것이다!!!


그래서 어떻게 해결할까?

1. 첫번째 방법으론 그냥 id를 붙여주는 것이다.

ForEach문 안에 id: \.self 를 추가로 넣어주어서 해당 배열의 요소들에 고유한 번호를 부여해 식별 가능하도록 만들었다. 

 

 

2. enumerated()를 활용해보자?
나처럼 배열에서 index를 활용하기 위한 또 다른 방법에 enumerated()가 있다는 것을 알게 되었다. 

https://developer.apple.com/documentation/swift/array/enumerated()

쌍의 시퀀스( n , x )를 반환한다. 여기서 n은 0에서 시작하는 연속 정수를 나타내고 x는 시퀀스의 요소를 나타낸다.
Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero and x represents an element of the sequence.

 

즉, enumerated()를 활용하면 n인 index와 x인 element를 반환받아 활용할 수 있는 것이다.

swift에서 제공해주는 문법으로 활용해봐야겠다 싶어서 해당 방법을 적용해보고자 했다. 

이런 오류가 발생했다.

Closure containing control flow statement cannot be used with result builder 'ViewBuilder'

조건문을 포함하는 클로져는 result builder인 'ViewBuilder'와 함께 사용할 수 없다.

 

조건문을 포함하는 클로저는 for-in 구문, result Builder인 ViewBuilder는 VStack이라서 해당 두개의 요소가 함께 쓰일 수 없다는 오류가 발생했다. VStack은 View를 리턴해야하는데 그 안에 조건문인 for문이 들어가서 오류가 생긴 것이다. 

 

따라서 이를 해결하기 위해서는 ForEach문을 활용해야 한다고 한다. 

rankingList.enumerated()를 활용하여 rankingList의 index와 element를 받아오게 하였다.