Skip to content

Commit fe18362

Browse files
authored
fix(installations, apple): update the plugin to support parallels method calling (#13256)
1 parent 2546971 commit fe18362

File tree

3 files changed

+124
-82
lines changed

3 files changed

+124
-82
lines changed

packages/firebase_app_installations/firebase_app_installations/ios/Classes/FirebaseInstallationsPlugin.swift

+82-70
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ let kFLTFirebaseInstallationsChannelName = "plugins.flutter.io/firebase_app_inst
1616
public class FirebaseInstallationsPluginSwift: FLTFirebasePlugin, FlutterPlugin {
1717
private var eventSink: FlutterEventSink?
1818
private var messenger: FlutterBinaryMessenger
19-
20-
var result: FLTFirebaseMethodCallResult?
21-
var streamHandler = [String: IdChangedStreamHandler?]()
22-
23-
var args = NSDictionary()
19+
private var streamHandler = [String: IdChangedStreamHandler?]()
2420

2521
init(messenger: FlutterBinaryMessenger) {
2622
self.messenger = messenger
@@ -45,60 +41,71 @@ public class FirebaseInstallationsPluginSwift: FLTFirebasePlugin, FlutterPlugin
4541

4642
/// Gets Installations instance for a Firebase App.
4743
/// - Returns: a Firebase Installations instance for the passed app from Dart
48-
func getInstallations() -> Installations {
49-
let app: FirebaseApp = FLTFirebasePlugin.firebaseAppNamed(args["appName"] as! String)!
44+
private func getInstallations(appName: String) -> Installations {
45+
let app: FirebaseApp = FLTFirebasePlugin.firebaseAppNamed(appName)!
5046
return Installations.installations(app: app)
5147
}
5248

5349
/// Gets Installations Id for an instance.
5450
/// - Parameter arguments: the arguments passed by the Dart calling method
5551
/// - Parameter result: the result instance used to send the result to Dart.
56-
func getId() {
57-
let instance: Installations = getInstallations()
52+
/// - Parameter errorBlock: the error block used to send the error to Dart.
53+
private func getId(arguments: NSDictionary, result: @escaping FlutterResult,
54+
errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) {
55+
let instance = getInstallations(appName: arguments["appName"] as! String)
5856
instance.installationID { (id: String?, error: Error?) in
59-
if error != nil {
60-
self.result!.error(nil, nil, nil, error)
57+
if let error {
58+
errorBlock(nil, nil, nil, error)
6159
} else {
62-
self.result!.success(id)
60+
result(id)
6361
}
6462
}
6563
}
6664

67-
/// Deletes Installations Id for an instance.
68-
func deleteId() {
69-
let instance: Installations = getInstallations()
65+
/// Deletes the Installations Id for an instance.
66+
/// - Parameter arguments: the arguments passed by the Dart calling method
67+
/// - Parameter result: the result instance used to send the result to Dart.
68+
/// - Parameter errorBlock: the error block used to send the error to Dart.
69+
private func deleteId(arguments: NSDictionary, result: @escaping FlutterResult,
70+
errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) {
71+
let instance = getInstallations(appName: arguments["appName"] as! String)
7072
instance.delete { (error: Error?) in
71-
if error != nil {
72-
self.result!.error(nil, nil, nil, error)
73+
if let error {
74+
errorBlock(nil, nil, nil, error)
7375
} else {
74-
self.result!.success(nil)
76+
result(nil)
7577
}
7678
}
7779
}
7880

79-
/// Gets the token Id for an instance.
80-
func getToken() {
81-
let instance: Installations = getInstallations()
82-
let forceRefresh: Bool = (args["forceRefresh"] as? Bool) ?? false
83-
84-
instance.authTokenForcingRefresh(
85-
forceRefresh,
86-
completion: { (tokenResult: InstallationsAuthTokenResult?, error: Error?) in
87-
if error != nil {
88-
self.result!.error(nil, nil, nil, error)
89-
} else {
90-
self.result!.success(tokenResult?.authToken)
91-
}
81+
/// Gets the Auth Token for an instance.
82+
/// - Parameter arguments: the arguments passed by the Dart calling method
83+
/// - Parameter result: the result instance used to send the result to Dart.
84+
/// - Parameter errorBlock: the error block used to send the error to Dart.
85+
private func getToken(arguments: NSDictionary, result: @escaping FlutterResult,
86+
errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) {
87+
let instance = getInstallations(appName: arguments["appName"] as! String)
88+
let forceRefresh = arguments["forceRefresh"] as? Bool ?? false
89+
instance
90+
.authTokenForcingRefresh(forceRefresh) { (tokenResult: InstallationsAuthTokenResult?,
91+
error: Error?) in
92+
if let error {
93+
errorBlock(nil, nil, nil, error)
94+
} else {
95+
result(tokenResult?.authToken)
96+
}
9297
}
93-
)
9498
}
9599

96-
/// Starts listening to Installation ID events for an instance.
97-
func registerIdChangeListener() {
98-
let instance: Installations = getInstallations()
99-
100-
let appName = (args["appName"] as! String)
101-
let eventChannelName: String = kFLTFirebaseInstallationsChannelName + "/token/" + appName
100+
/// Registers a listener for changes in the Installations Id.
101+
/// - Parameter arguments: the arguments passed by the Dart calling method
102+
/// - Parameter result: the result instance used to send the result to Dart.
103+
/// - Parameter errorBlock: the error block used to send the error to Dart.
104+
private func registerIdChangeListener(arguments: NSDictionary, result: @escaping FlutterResult,
105+
errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) {
106+
let instance = getInstallations(appName: arguments["appName"] as! String)
107+
let appName = arguments["appName"] as! String
108+
let eventChannelName = kFLTFirebaseInstallationsChannelName + "/token/" + appName
102109

103110
let eventChannel = FlutterEventChannel(name: eventChannelName, binaryMessenger: messenger)
104111

@@ -108,10 +115,10 @@ public class FirebaseInstallationsPluginSwift: FLTFirebasePlugin, FlutterPlugin
108115

109116
eventChannel.setStreamHandler(streamHandler[eventChannelName]!)
110117

111-
result?.success(eventChannelName)
118+
result(eventChannelName)
112119
}
113120

114-
func mapInstallationsErrorCodes(code: UInt) -> NSString {
121+
private func mapInstallationsErrorCodes(code: UInt) -> NSString {
115122
let error = InstallationsErrorCode(InstallationsErrorCode
116123
.Code(rawValue: Int(code)) ?? InstallationsErrorCode.unknown)
117124

@@ -130,44 +137,49 @@ public class FirebaseInstallationsPluginSwift: FLTFirebasePlugin, FlutterPlugin
130137
}
131138

132139
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
133-
let args = call.arguments as! NSDictionary
134-
135-
let errorBlock: FLTFirebaseMethodCallErrorBlock = { (code, message, details, error: Error?) in
136-
var errorDetails = [String: Any?]()
137-
138-
errorDetails["code"] = code ?? self
139-
.mapInstallationsErrorCodes(code: UInt((error! as NSError).code))
140-
errorDetails["message"] = message ?? error?
141-
.localizedDescription ?? "An unknown error has occurred."
142-
errorDetails["additionalData"] = details
143-
144-
if code == "unknown" {
145-
NSLog(
146-
"FLTFirebaseInstallations: An error occurred while calling method %@",
147-
call.method
148-
)
149-
}
150-
151-
result(FLTFirebasePlugin.createFlutterError(fromCode: errorDetails["code"] as! String,
152-
message: errorDetails["message"] as! String,
153-
optionalDetails: errorDetails[
154-
"additionalData"
155-
] as? [AnyHashable: Any],
156-
andOptionalNSError: error))
140+
guard let args = call.arguments as? NSDictionary else {
141+
result(FlutterError(
142+
code: "invalid-arguments",
143+
message: "Arguments are not a dictionary",
144+
details: nil
145+
))
146+
return
157147
}
158148

159-
self.result = .create(success: result, andErrorBlock: errorBlock)
160-
self.args = args
149+
let errorBlock: FLTFirebaseMethodCallErrorBlock = { (code, message, details,
150+
error: Error?) in
151+
var errorDetails = [String: Any?]()
152+
153+
errorDetails["code"] = code ?? self
154+
.mapInstallationsErrorCodes(code: UInt((error! as NSError).code))
155+
errorDetails["message"] = message ?? error?
156+
.localizedDescription ?? "An unknown error has occurred."
157+
errorDetails["additionalData"] = details
158+
159+
if code == "unknown" {
160+
NSLog(
161+
"FLTFirebaseInstallations: An error occurred while calling method %@",
162+
call.method
163+
)
164+
}
165+
166+
result(FLTFirebasePlugin.createFlutterError(fromCode: errorDetails["code"] as! String,
167+
message: errorDetails["message"] as! String,
168+
optionalDetails: errorDetails[
169+
"additionalData"
170+
] as? [AnyHashable: Any],
171+
andOptionalNSError: error))
172+
}
161173

162174
switch call.method {
163175
case "FirebaseInstallations#getId":
164-
getId()
176+
getId(arguments: args, result: result, errorBlock: errorBlock)
165177
case "FirebaseInstallations#delete":
166-
deleteId()
178+
deleteId(arguments: args, result: result, errorBlock: errorBlock)
167179
case "FirebaseInstallations#getToken":
168-
getToken()
180+
getToken(arguments: args, result: result, errorBlock: errorBlock)
169181
case "FirebaseInstallations#registerIdChangeListener":
170-
registerIdChangeListener()
182+
registerIdChangeListener(arguments: args, result: result, errorBlock: errorBlock)
171183
default:
172184
result(FlutterMethodNotImplemented)
173185
}

packages/firebase_app_installations/firebase_app_installations/ios/Classes/IdChangedStreamHandler.swift

+28-12
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,55 @@ class IdChangedStreamHandler: NSObject, FlutterStreamHandler {
1919

2020
init(instance: Installations) {
2121
self.instance = instance
22+
super.init()
23+
}
24+
25+
deinit {
26+
if let observer = installationIDObserver {
27+
NotificationCenter.default.removeObserver(observer)
28+
}
2229
}
2330

2431
func handleIdChange() {
25-
var events = [String: String]()
26-
instance.installationID { (newId: String?, error: Error?) in
27-
if error != nil {
28-
self.eventSink!(FlutterError(
32+
instance.installationID { [weak self] (newId: String?, error: Error?) in
33+
guard let self else { return }
34+
35+
if let error {
36+
self.eventSink?(FlutterError(
2937
code: "unknown",
30-
message: error?.localizedDescription,
31-
details: ["code": "unknown", "message": error?.localizedDescription]
38+
message: error.localizedDescription,
39+
details: ["code": "unknown", "message": error.localizedDescription]
3240
))
33-
} else if newId != self.installationsId {
34-
self.installationsId = newId!
35-
events["token"] = self.installationsId
36-
self.eventSink!(events)
41+
} else if let newId, newId != self.installationsId {
42+
self.installationsId = newId
43+
self.eventSink?(["token": self.installationsId])
3744
}
3845
}
3946
}
4047

4148
public func onListen(withArguments _: Any?,
4249
eventSink events: @escaping FlutterEventSink) -> FlutterError? {
4350
eventSink = events
51+
4452
installationIDObserver = NotificationCenter.default.addObserver(
4553
forName: .InstallationIDDidChange,
4654
object: nil,
4755
queue: nil
48-
) { _ in
49-
self.handleIdChange()
56+
) { [weak self] _ in
57+
self?.handleIdChange()
5058
}
59+
60+
// Trigger initial event when listener is added
61+
handleIdChange()
62+
5163
return nil
5264
}
5365

5466
public func onCancel(withArguments _: Any?) -> FlutterError? {
67+
if let observer = installationIDObserver {
68+
NotificationCenter.default.removeObserver(observer)
69+
installationIDObserver = nil
70+
}
5571
eventSink = nil
5672
return nil
5773
}

tests/integration_test/firebase_app_installations/firebase_app_installations_e2e_test.dart

+14
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ void main() {
3131
skip: defaultTargetPlatform == TargetPlatform.macOS,
3232
);
3333

34+
test(
35+
'running get id in parallel',
36+
() async {
37+
final ids = await Future.wait([
38+
FirebaseInstallations.instance.getId(),
39+
FirebaseInstallations.instance.getId(),
40+
FirebaseInstallations.instance.getId(),
41+
FirebaseInstallations.instance.getId(),
42+
FirebaseInstallations.instance.getId(),
43+
]);
44+
expect(ids, isNotNull);
45+
},
46+
);
47+
3448
test(
3549
'.delete',
3650
() async {

0 commit comments

Comments
 (0)