Skip to content

Commit 2405f6a

Browse files
authored
[pigeon] swift equality methods (#8971)
adds == and hash methods to classes
1 parent 267ac7b commit 2405f6a

29 files changed

+997
-453
lines changed

packages/pigeon/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 25.3.0
2+
3+
* [swift] Adds equality methods to generated data classes.
4+
* [dart, kotlin] Shortens equality methods.
5+
16
## 25.2.0
27

38
* [kotlin] Adds equality methods to generated data classes.

packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,35 @@ import io.flutter.plugin.common.StandardMethodCodec
1212
import java.io.ByteArrayOutputStream
1313
import java.nio.ByteBuffer
1414

15+
private fun deepEqualsEventChannelMessages(a: Any?, b: Any?): Boolean {
16+
if (a is ByteArray && b is ByteArray) {
17+
return a.contentEquals(b)
18+
}
19+
if (a is IntArray && b is IntArray) {
20+
return a.contentEquals(b)
21+
}
22+
if (a is LongArray && b is LongArray) {
23+
return a.contentEquals(b)
24+
}
25+
if (a is DoubleArray && b is DoubleArray) {
26+
return a.contentEquals(b)
27+
}
28+
if (a is Array<*> && b is Array<*>) {
29+
return a.size == b.size && a.indices.all { deepEqualsEventChannelMessages(a[it], b[it]) }
30+
}
31+
if (a is List<*> && b is List<*>) {
32+
return a.size == b.size && a.indices.all { deepEqualsEventChannelMessages(a[it], b[it]) }
33+
}
34+
if (a is Map<*, *> && b is Map<*, *>) {
35+
return a.size == b.size &&
36+
a.all {
37+
(b as Map<Any?, Any?>).containsKey(it.key) &&
38+
deepEqualsEventChannelMessages(it.value, b[it.key])
39+
}
40+
}
41+
return a == b
42+
}
43+
1544
/**
1645
* Generated class from Pigeon that represents data sent in messages. This class should not be
1746
* extended by any user class outside of the generated file.
@@ -39,7 +68,7 @@ data class IntEvent(val data: Long) : PlatformEvent() {
3968
if (this === other) {
4069
return true
4170
}
42-
return data == other.data
71+
return deepEqualsEventChannelMessages(toList(), other.toList())
4372
}
4473

4574
override fun hashCode(): Int = toList().hashCode()
@@ -67,7 +96,7 @@ data class StringEvent(val data: String) : PlatformEvent() {
6796
if (this === other) {
6897
return true
6998
}
70-
return data == other.data
99+
return deepEqualsEventChannelMessages(toList(), other.toList())
71100
}
72101

73102
override fun hashCode(): Int = toList().hashCode()

packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,14 @@ private fun deepEqualsMessages(a: Any?, b: Any?): Boolean {
6262
if (a is Array<*> && b is Array<*>) {
6363
return a.size == b.size && a.indices.all { deepEqualsMessages(a[it], b[it]) }
6464
}
65+
if (a is List<*> && b is List<*>) {
66+
return a.size == b.size && a.indices.all { deepEqualsMessages(a[it], b[it]) }
67+
}
6568
if (a is Map<*, *> && b is Map<*, *>) {
6669
return a.size == b.size &&
67-
a.keys.all { (b as Map<Any?, Any?>).containsKey(it) && deepEqualsMessages(a[it], b[it]) }
70+
a.all {
71+
(b as Map<Any?, Any?>).containsKey(it.key) && deepEqualsMessages(it.value, b[it.key])
72+
}
6873
}
6974
return a == b
7075
}
@@ -113,10 +118,7 @@ data class MessageData(
113118
if (this === other) {
114119
return true
115120
}
116-
return name == other.name &&
117-
description == other.description &&
118-
code == other.code &&
119-
deepEqualsMessages(data, other.data)
121+
return deepEqualsMessages(toList(), other.toList())
120122
}
121123

122124
override fun hashCode(): Int = toList().hashCode()

packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,68 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
2323
return value as! T?
2424
}
2525

26+
func deepEqualsEventChannelMessages(_ lhs: Any?, _ rhs: Any?) -> Bool {
27+
let cleanLhs = nilOrValue(lhs) as Any?
28+
let cleanRhs = nilOrValue(rhs) as Any?
29+
switch (cleanLhs, cleanRhs) {
30+
case (nil, nil):
31+
return true
32+
33+
case (nil, _), (_, nil):
34+
return false
35+
36+
case is (Void, Void):
37+
return true
38+
39+
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
40+
return cleanLhsHashable == cleanRhsHashable
41+
42+
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
43+
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
44+
for (index, element) in cleanLhsArray.enumerated() {
45+
if !deepEqualsEventChannelMessages(element, cleanRhsArray[index]) {
46+
return false
47+
}
48+
}
49+
return true
50+
51+
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
52+
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
53+
for (key, cleanLhsValue) in cleanLhsDictionary {
54+
guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
55+
if !deepEqualsEventChannelMessages(cleanLhsValue, cleanRhsDictionary[key]!) {
56+
return false
57+
}
58+
}
59+
return true
60+
61+
default:
62+
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
63+
return false
64+
}
65+
}
66+
67+
func deepHashEventChannelMessages(value: Any?, hasher: inout Hasher) {
68+
if let valueList = value as? [AnyHashable] {
69+
for item in valueList { deepHashEventChannelMessages(value: item, hasher: &hasher) }
70+
return
71+
}
72+
73+
if let valueDict = value as? [AnyHashable: AnyHashable] {
74+
for key in valueDict.keys {
75+
hasher.combine(key)
76+
deepHashEventChannelMessages(value: valueDict[key]!, hasher: &hasher)
77+
}
78+
return
79+
}
80+
81+
if let hashableValue = value as? AnyHashable {
82+
hasher.combine(hashableValue.hashValue)
83+
}
84+
85+
return hasher.combine(String(describing: value))
86+
}
87+
2688
/// Generated class from Pigeon that represents data sent in messages.
2789
/// This protocol should not be extended by any user class outside of the generated file.
2890
protocol PlatformEvent {
@@ -46,6 +108,12 @@ struct IntEvent: PlatformEvent {
46108
data
47109
]
48110
}
111+
static func == (lhs: IntEvent, rhs: IntEvent) -> Bool {
112+
return deepEqualsEventChannelMessages(lhs.toList(), rhs.toList())
113+
}
114+
func hash(into hasher: inout Hasher) {
115+
deepHashEventChannelMessages(value: toList(), hasher: &hasher)
116+
}
49117
}
50118

51119
/// Generated class from Pigeon that represents data sent in messages.
@@ -65,6 +133,12 @@ struct StringEvent: PlatformEvent {
65133
data
66134
]
67135
}
136+
static func == (lhs: StringEvent, rhs: StringEvent) -> Bool {
137+
return deepEqualsEventChannelMessages(lhs.toList(), rhs.toList())
138+
}
139+
func hash(into hasher: inout Hasher) {
140+
deepHashEventChannelMessages(value: toList(), hasher: &hasher)
141+
}
68142
}
69143

70144
private class EventChannelMessagesPigeonCodecReader: FlutterStandardReader {

packages/pigeon/example/app/ios/Runner/Messages.g.swift

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,75 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
7373
return value as! T?
7474
}
7575

76+
func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool {
77+
let cleanLhs = nilOrValue(lhs) as Any?
78+
let cleanRhs = nilOrValue(rhs) as Any?
79+
switch (cleanLhs, cleanRhs) {
80+
case (nil, nil):
81+
return true
82+
83+
case (nil, _), (_, nil):
84+
return false
85+
86+
case is (Void, Void):
87+
return true
88+
89+
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
90+
return cleanLhsHashable == cleanRhsHashable
91+
92+
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
93+
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
94+
for (index, element) in cleanLhsArray.enumerated() {
95+
if !deepEqualsMessages(element, cleanRhsArray[index]) {
96+
return false
97+
}
98+
}
99+
return true
100+
101+
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
102+
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
103+
for (key, cleanLhsValue) in cleanLhsDictionary {
104+
guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
105+
if !deepEqualsMessages(cleanLhsValue, cleanRhsDictionary[key]!) {
106+
return false
107+
}
108+
}
109+
return true
110+
111+
default:
112+
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
113+
return false
114+
}
115+
}
116+
117+
func deepHashMessages(value: Any?, hasher: inout Hasher) {
118+
if let valueList = value as? [AnyHashable] {
119+
for item in valueList { deepHashMessages(value: item, hasher: &hasher) }
120+
return
121+
}
122+
123+
if let valueDict = value as? [AnyHashable: AnyHashable] {
124+
for key in valueDict.keys {
125+
hasher.combine(key)
126+
deepHashMessages(value: valueDict[key]!, hasher: &hasher)
127+
}
128+
return
129+
}
130+
131+
if let hashableValue = value as? AnyHashable {
132+
hasher.combine(hashableValue.hashValue)
133+
}
134+
135+
return hasher.combine(String(describing: value))
136+
}
137+
76138
enum Code: Int {
77139
case one = 0
78140
case two = 1
79141
}
80142

81143
/// Generated class from Pigeon that represents data sent in messages.
82-
struct MessageData {
144+
struct MessageData: Hashable {
83145
var name: String? = nil
84146
var description: String? = nil
85147
var code: Code
@@ -107,6 +169,12 @@ struct MessageData {
107169
data,
108170
]
109171
}
172+
static func == (lhs: MessageData, rhs: MessageData) -> Bool {
173+
return deepEqualsMessages(lhs.toList(), rhs.toList())
174+
}
175+
func hash(into hasher: inout Hasher) {
176+
deepHashMessages(value: toList(), hasher: &hasher)
177+
}
110178
}
111179

112180
private class MessagesPigeonCodecReader: FlutterStandardReader {

packages/pigeon/example/app/lib/src/event_channel_messages.g.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
1111
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
1212
import 'package:flutter/services.dart';
1313

14+
bool _deepEquals(Object? a, Object? b) {
15+
if (a is List && b is List) {
16+
return a.length == b.length &&
17+
a.indexed
18+
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
19+
}
20+
if (a is Map && b is Map) {
21+
return a.length == b.length &&
22+
a.entries.every((MapEntry<Object?, Object?> entry) =>
23+
(b as Map<Object?, Object?>).containsKey(entry.key) &&
24+
_deepEquals(entry.value, b[entry.key]));
25+
}
26+
return a == b;
27+
}
28+
1429
sealed class PlatformEvent {}
1530

1631
class IntEvent extends PlatformEvent {
@@ -46,7 +61,7 @@ class IntEvent extends PlatformEvent {
4661
if (identical(this, other)) {
4762
return true;
4863
}
49-
return data == other.data;
64+
return _deepEquals(encode(), other.encode());
5065
}
5166

5267
@override
@@ -87,7 +102,7 @@ class StringEvent extends PlatformEvent {
87102
if (identical(this, other)) {
88103
return true;
89104
}
90-
return data == other.data;
105+
return _deepEquals(encode(), other.encode());
91106
}
92107

93108
@override

packages/pigeon/example/app/lib/src/messages.g.dart

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@ bool _deepEquals(Object? a, Object? b) {
3636
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
3737
}
3838
if (a is Map && b is Map) {
39-
final Iterable<Object?> keys = (a as Map<Object?, Object?>).keys;
4039
return a.length == b.length &&
41-
keys.every((Object? key) =>
42-
(b as Map<Object?, Object?>).containsKey(key) &&
43-
_deepEquals(a[key], b[key]));
40+
a.entries.every((MapEntry<Object?, Object?> entry) =>
41+
(b as Map<Object?, Object?>).containsKey(entry.key) &&
42+
_deepEquals(entry.value, b[entry.key]));
4443
}
4544
return a == b;
4645
}
@@ -98,10 +97,7 @@ class MessageData {
9897
if (identical(this, other)) {
9998
return true;
10099
}
101-
return name == other.name &&
102-
description == other.description &&
103-
code == other.code &&
104-
_deepEquals(data, other.data);
100+
return _deepEquals(encode(), other.encode());
105101
}
106102

107103
@override

packages/pigeon/lib/src/dart/dart_generator.dart

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -351,16 +351,7 @@ class DartGenerator extends StructuredGenerator<InternalDartOptions> {
351351
indent.writeScoped('if (identical(this, other)) {', '}', () {
352352
indent.writeln('return true;');
353353
});
354-
indent.writeScoped('return ', '', () {
355-
indent.format(
356-
classDefinition.fields
357-
.map((NamedType field) => isCollectionType(field.type)
358-
? '_deepEquals(${field.name}, other.${field.name})'
359-
: '${field.name} == other.${field.name}')
360-
.join('\n&& '),
361-
trailingNewline: false);
362-
indent.addln(';');
363-
}, addTrailingNewline: false);
354+
indent.writeln('return _deepEquals(encode(), other.encode());');
364355
});
365356
indent.newln();
366357
indent.writeln('@override');
@@ -1083,9 +1074,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger;
10831074
generatorOptions.testOut != null) {
10841075
_writeWrapResponse(generatorOptions, root, indent);
10851076
}
1086-
if (root.classes.isNotEmpty &&
1087-
root.classes.any((Class dataClass) => dataClass.fields
1088-
.any((NamedType field) => isCollectionType(field.type)))) {
1077+
if (root.classes.isNotEmpty) {
10891078
_writeDeepEquals(indent);
10901079
}
10911080
}
@@ -1116,14 +1105,13 @@ bool _deepEquals(Object? a, Object? b) {
11161105
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
11171106
}
11181107
if (a is Map && b is Map) {
1119-
final Iterable<Object?> keys = (a as Map<Object?, Object?>).keys;
1120-
return a.length == b.length && keys.every((Object? key) =>
1121-
(b as Map<Object?, Object?>).containsKey(key) &&
1122-
_deepEquals(a[key], b[key]));
1108+
return a.length == b.length && a.entries.every((MapEntry<Object?, Object?> entry) =>
1109+
(b as Map<Object?, Object?>).containsKey(entry.key) &&
1110+
_deepEquals(entry.value, b[entry.key]));
11231111
}
11241112
return a == b;
11251113
}
1126-
''');
1114+
''');
11271115
}
11281116

11291117
void _writeCreateConnectionError(Indent indent) {

packages/pigeon/lib/src/generator_tools.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import 'generator.dart';
1515
/// The current version of pigeon.
1616
///
1717
/// This must match the version in pubspec.yaml.
18-
const String pigeonVersion = '25.2.0';
18+
const String pigeonVersion = '25.3.0';
1919

2020
/// Read all the content from [stdin] to a String.
2121
String readStdin() {

0 commit comments

Comments
 (0)