New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Swift] reference type and value type conformances to IGListDiffable #35
Comments
Ah, interesting. But, |
@jessesquires to my knowledge all ObjC protocols in Swift inherit from Just tried w/ this: struct StructUser: IGListDiffable {
let name: String
let id: Int
} And the compiler errors. |
I see. I thought we declared this as Seems like this should be consider a Swift bug to me. We should search https://bugs.swift.org or ask around. Just looked through the inter-op docs and didn't see anything about this. Intended behavior or not, for now I'm not sure if we can support swift value types. Maybe we could provide a "Box" for swift value types: public final class DiffableBox<T: Equatable>: IGListDiffable {
let value: T
let identifier: Any
let equal: (T, T) -> Bool
init(value: T, identifier: Any, equal: @escaping (T, T) -> Bool) {
self.value = value
self.identifier = identifier
self.equal = equal
}
// IGListDiffable
func diffIdentifier() -> Any {
return identifier
}
func isEqual(obj: Any) -> Bool {
if let other = obj as? T {
return equal(value, other)
}
return false
}
}
// Usage
let str = "my string"
let diffBox = DiffableBox(value: str, identifier: str, equal: ==) Just a rough sketch, but you init with your value type, an identifier, and a equal closure/func. You can package Swift + ObjC sources in a framework. If we add this, then this class should only be exposed to Swift clients and everything should just work. Clients could |
Got an answer 😄 |
Bummer, makes sense tho. I wish there was something like Boxing is an awesome idea though. Probably good to provide something like that. |
@jessesquires check out h/t to @nlutsenko https://twitter.com/nlutsenko/status/782652105530019840 |
Interesting.
|
Ah yes, this was removed in Swift 3: Swift 2.2 docs: http://swiftdoc.org/v2.2/protocol/_ObjectiveCBridgeable/ |
Ah, perhaps not removed, but now properly https://github.com/apple/swift-evolution/blob/master/proposals/0058-objectivecbridgeable.md https://lists.swift.org/pipermail/swift-evolution-announce/2016-April/000095.html |
Ok good to know. I was looking at it more over the weekend too and don't think it would've helped. I think boxing is going to be the way to go for now. |
Ah, |
Messed around w/ it, not sure how we can add an |
Is there still no way we can have a swift struct conform to |
@matthewcheok nope 😕 when using Swift objects have to be a |
Going to close this since there's nothing actionable right now (and for the foreseeable future) |
Inspired by @jessesquires I will put my solution in here for enabling swift value types to conform to Diffable. I ended up with a new Swift protocol I then wrap diff'ing around a struct called /**
A diffable value type that can be used in conjunction with
`DiffUtility` to perform a diff between two result sets.
*/
public protocol Diffable: Equatable {
/**
Returns a key that uniquely identifies the object.
- returns: A key that can be used to uniquely identify the object.
- note: Two objects may share the same identifier, but are not equal.
- warning: This value should never be mutated.
*/
var diffIdentifier: String { get }
}
/**
Performs a diff operation between two sets of `ItemDiffable` results.
*/
public struct DiffUtility {
public struct DiffResult {
public typealias Move = (from: Int, to: Int)
public let inserts: [Int]
public let deletions: [Int]
public let updates: [Int]
public let moves: [Move]
public let oldIndexForID: (_ id: String) -> Int
public let newIndexForID: (_ id: String) -> Int
}
public static func diff<T: Diffable>(originalItems: [T], newItems: [T]) -> DiffResult {
let old = originalItems.map({ DiffableBox(value: $0, identifier: $0.diffIdentifier as NSObjectProtocol, equal: ==) })
let new = newItems.map({ DiffableBox(value: $0, identifier: $0.diffIdentifier as NSObjectProtocol, equal: ==) })
let result = IGListDiff(old, new, .equality)
let inserts = Array(result.inserts)
let deletions = Array(result.deletes)
let updates = Array(result.updates)
let moves: [DiffResult.Move] = result.moves.map({ (from: $0.from, to: $0.to) })
let oldIndexForID: (_ id: String) -> Int = { id in
return result.oldIndex(forIdentifier: NSString(string: id))
}
let newIndexForID: (_ id: String) -> Int = { id in
return result.newIndex(forIdentifier: NSString(string: id))
}
return DiffResult(inserts: inserts, deletions: deletions, updates: updates, moves: moves, oldIndexForID: oldIndexForID, newIndexForID: newIndexForID)
}
}
private final class DiffableBox<T: Diffable>: IGListDiffable {
let value: T
let identifier: NSObjectProtocol
let equal: (T, T) -> Bool
init(value: T, identifier: NSObjectProtocol, equal: @escaping(T, T) -> Bool) {
self.value = value
self.identifier = identifier
self.equal = equal
}
// IGListDiffable
func diffIdentifier() -> NSObjectProtocol {
return identifier
}
func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
if let other = object as? DiffableBox<T> {
return equal(value, other.value)
}
return false
}
} |
@danielgalasko 😲 this is super cool! cc @jessesquires |
@danielgalasko reading this took me some time, before i understood it. Could you put here small example of usage? |
sure thing @CurlyHeir but its very similar to how you would use IGListKit normally. So lets start with a struct: struct TestDiff: Diffable {
var name: String
let id: String
var diffIdentifier: String {
return id
}
static func ==(lhs: TestDiff, rhs: TestDiff) -> Bool {
return lhs.name == rhs.name
}
} let bob = TestDiff(name: "Bob", id: "1")
let tigger = TestDiff(name: "tigger", id: "2")
let initial = [bob]
let new = [bob, tigger]
let diff = DiffUtility.diff(originalItems: initial, newItems: new)
//diff.inserts == 1 Not sure if thats what you were asking but thats how I use it |
Hello @danielgalasko ! How do you use |
@Eke you will see I created diffableItems.map({ DiffableBox(value: $0, identifier: $0.diffIdentifier as NSObjectProtocol, equal: ==) }) Kinda like that :) |
Just created an extension to
|
is this still the way to go ? any full example please ? @danielgalasko |
Chiming in here. Would be fantastic to simply use @rnystrom Instagram looking to adopt Swift anytime soon? 😂 |
@levi that would be awesome, any progress ? the |
Just letting you all know that I have a Swift bridge layer idea in progress that will handle all of this under the hood, letting you use Swift structs with IGListKit! Sent with GitHawk |
Thanks for the update, Ryan! |
@rnystrom First off, this is such a killer library you've built! I'm just wondering if there's been any movement on the Swift bridge layer mentioned above for enabling the use of Swift structs? Thanks! |
@vibrazy HI @danielgalasko Seems When I want to use them in a But when I try to use in And Should you think make Thanks for your help! |
@Arcovv I think I know the problem you are having. When you're making the switch for your example of concrete diffablebox vs a non concrete edit: after rereading your comment, I suspect I have a newer version of the diffablebox, so maybe what I'm saying doesn't apply |
Any progress officially supporting this? @rnystrom |
@rnystrom Any progress supporting this? |
2019 Already, is this still a WIP? |
I would just look into using |
Is 2022. Nothing yet ? |
The handy
NSObject+IGListDiffable
category doesn't really help with Swift objects likeString
andInt
. This seems a little tricky becauseString
is astruct
but is convertible to anNSString
. Doing that conversion gives us theNSObject<IGListDiffable>
conformance, but that's kind of lame. I wonder if there's a way we can get this to work a little better?For example, the Swift 3.0 compiler wont allow this:
But you can get this to work a few ways:
Also adding an extension to
String
isn't easy either, sinceString
is a struct andIGListDiffable
is an Objective-C protocol...The text was updated successfully, but these errors were encountered: