I got a build error for below code after using xcode 16
Cast from 'ObservableCollection<E>.SectionsChange' (aka 'CollectionChange<Int, ObservableArray<E>>') to unrelated type 'ObservableCollection<Int>.SectionsChange' (aka 'CollectionChange<Int, ObservableArray<Int>>') always fails
for the section protocol method I implemented, first cast is still working
class ObservableCollectionDelegateMock: ObservableCollectionDelegate {
var collectionDidRowBatchUpdateAtExpect = LIExpect()
var collectionDidSectionBatchUpdateAtExpect = LIExpect()
var rowBatchUpdates: ObservableCollection<Int>.RowsChange?
var sectionBatchUpdates: ObservableCollection<Int>.SectionsChange?
func collection<E>(_ collection: ObservableCollection<E>, rowsDidChange change: ObservableCollection<E>.RowsChange) {
collectionDidRowBatchUpdateAtExpect.called(actualParams: nil)
rowBatchUpdates = change as? ObservableCollection<Int>.RowsChange
}
func collection<E>(_ collection: ObservableCollection<E>, sectionsDidChange change: ObservableCollection<E>.SectionsChange) {
collectionDidSectionBatchUpdateAtExpect.called(actualParams: nil)
sectionBatchUpdates = change as? ObservableCollection<Int>.SectionsChange
}
}
Here is the definition of the ObservableCollectionDelegate
which is the protocol delcare these 2 methods, that sublass to override
public protocol ObservableCollectionDelegate: AnyObject {
func collection<E>(_ collection: ObservableCollection<E>, rowsDidChange change: ObservableCollection<E>.RowsChange)
func collection<E>(_ collection: ObservableCollection<E>, sectionsDidChange change: ObservableCollection<E>.SectionsChange)
}
here is the definition of the these 2 alias.
class ObservableCollection<Element> {
// MARK: - Nested Types
public typealias RowsChange = CollectionChange<IndexPath, Element>
public typealias SectionsChange = CollectionChange<Int, ObservableArray<Element>>
.......
}
and this is the define of ObservableArray
public class ObservableArray<T> {
// MARK: - Public Properties
public var count: Int {
return elements.count
}
public var isEmpty: Bool {
return elements.isEmpty
}
public var hasElements: Bool {
return !elements.isEmpty
}
/// Returns the snapshot of current state as a normal array. (Essentially returning the copy of itself).
public var snapshot: [T] {
return elements
}
// MARK: - Internal Properties
internal(set) public var elements: [T]
// MARK: - Lifecycle
public init<S>(elements: S) where S: Sequence, S.Element == T {
self.elements = [T](elements)
}
......
From what I am understanding, seems for xcode 16, then can’t case the generic type by compiler if there are nested generic types.
which is our case is
ObservableCollection.SectionsChange’ (aka ‘CollectionChange<Int, ObservableArray>’)
wonder if there is anyway to go around this? or this is what swift 6 forbid to do?
thanks
====================================================
To make the code more easy to reproduce, I create a demo to have the same issue
import UIKit
import Foundation
public struct CollectionChange<Index: Hashable, Value> {}
public class ObservableArray<T> {
// MARK: - Internal Properties
internal(set) public var elements: [T]
// MARK: - Lifecycle
public init<S>(elements: S) where S: Sequence, S.Element == T {
self.elements = [T](elements)
}
public convenience init() {
self.init(elements: [])
}
}
open class ObservableCollection<Element> {
public typealias RowsChange = CollectionChange<IndexPath, Element>
public typealias SectionsChange = CollectionChange<Int, ObservableArray<Element>>
}
public protocol ObservableCollectionDelegate: AnyObject {
func collection<E>(_ collection: ObservableCollection<E>, rowsDidChange change: ObservableCollection<E>.RowsChange)
func collection<E>(_ collection: ObservableCollection<E>, sectionsDidChange change: ObservableCollection<E>.SectionsChange)
}
class ObservableCollectionDelegateMock: ObservableCollectionDelegate {
var rowBatchUpdates: ObservableCollection<Int>.RowsChange?
var sectionBatchUpdates: ObservableCollection<Int>.SectionsChange?
func collection<E>(_ collection: ObservableCollection<E>, rowsDidChange change: ObservableCollection<E>.RowsChange) {
rowBatchUpdates = change as? ObservableCollection<Int>.RowsChange
}
func collection<E>(_ collection: ObservableCollection<E>, sectionsDidChange change: ObservableCollection<E>.SectionsChange) {
sectionBatchUpdates = change as? ObservableCollection<Int>.SectionsChange
}
}
you can try in your local, maybe just put in playground. I tried for xcode 15, it works fine with Swift 5
and for xcode 16 with swift 6, it has the error
Cast from 'ObservableCollection<E>.SectionsChange' (aka 'CollectionChange<Int, ObservableArray<E>>') to unrelated type 'ObservableCollection<Int>.SectionsChange' (aka 'CollectionChange<Int, ObservableArray<Int>>') always fails
2
Answers
A different approach, since you are expecting the generic type to be an Int while casting, all the generic type E should be an Int or same type.
So you can change the ObservableCollectionDelegateMock to have a generic element
Associated type for protocol
if you use like below, then the collection function element might not be same as associatedType