seong_hye, the developer

오류 해결) swift:600: Fatal error: Index out of range 본문

IOS/Error

오류 해결) swift:600: Fatal error: Index out of range

seong_hye 2022. 10. 28.

 

❗️ "Index out of range" error 해결 ❗️

Swift에서 가장 자주 마주하게 되는 오류라고 할 수 있음

 

Index out of range = 배열이나 컬렉션의 범위를 벗어난 인덱스에 접근할 때 발생하는 에러


🔹 자주 발생하는 원인

- 인덱스를 하드코딩 했는데 값이 없을 경우

let arrays = [1, 2, 3, 4, 5]

for i in 0...5 { // error ~> 갯수는 5개지만 인덱스는 0~4까지임

}

 

- 빈 배열에서 접근 시도

var arrays = []

print(arrays[0]) // error ~> 아직 값이 들어오지 않았음

 

- 비동기 작업에서 배열 변경 후 접근

var arrays = []

arrays.getArray()
...
if ( ... ) {
	arrays.removeAll() // ~> 어떤 경우 배열을 삭제하는 코드
	...
	print(arrays) // error ~> 경우의 수를 잘못 설정해 지워진 뒤에 접근 해 오류 발생

 

- 잘못된 루프 범위 사용

for i in 0..array.count { //error ~> 범위가 잘못 설정되어 오류 발생
	...
}

🔹 안전하게 사용하는 방법

➡️ 배열의 count로 먼저 확인

if index < array.count {
	let value = array[index]
}

 

➡️ indices 속성 사용

if array.indices.contains(index) {
	print(array[index])
}

 

➡️ 옵셔널 방식으로 안전하게

Swift에는 기본적으로 없지만 Array에 확장을 추가하면 편리하게 사용 가능

let value = array[safe:index] // 확장 사용 시 안전

extension Array {
	subscript(safe index: Int) -> Element? {
    	return indices.contains(index) ? self[index] : nil
    }
}

⚠️ 하지만 내가 오류가 발생한 이유

문제 상황 요약 (Ex)

- 1번 뷰 (ListView)

-> 배열을 사용해서 리스트를 보여줌 (items)

 

- 2번 뷰 (DetailView)

items 배열을 값 복사로 파라미터로 전달

 

- 2번 뷰에서 수정 후 1번 뷰로 돌아오자

-> 1번 뷰가 다시 리렌더링되며 index out of range 발생

struct ListView: View {
	@State private var items = [1,2,3,4,5]
    @State private var selectedIndex = 1
    
    var body: some View {
    	NavigationLink(destination:SecondView(items: items)) {
        	Text(items[selectedIndex]) // error 발생
        }
    }
}

✅ 오류가 발생한 이유

SwiftUI는 상태가 변하면 뷰를 다시 계산하고 그리기 때문에 다음과 같은 상황이 생긴 것

 

1. 2번 뷰에서 조작한 결과 (ex. delete)가 상태를 변경함 -> 이 변경이 @State, @ObservedObject를 통해 1번 뷰에도 영향을 줌2. SwiftUI는 1번 뷰를 다시 렌더링3. 그런데 1번 뷰 내부에서 여전히 이전 인덱스 (ex. items[selectedIndex])를 사용4. 현재 items.count가 줄어든 상태 -> index out of range 오류 발생


✅ 해결한 방법

상태 변화에 따라 데이터 다시 받아올 수 있도록 수정

.onChange(of: items) { newItems in
	if !newItems.indices.contains(selectedIndex) {
    	selectedIndex = 0
    }
}


onChange를 사용해서 배열 자체나 관련된 사항의 변화가 생기면

새로 받아오거나 초기화 할 수 있도록 수정함


 

Comments