Skip to content

Commit 2a8dd10

Browse files
committed
Refactor service initialization and improve reconnect logic
Refactored AppwriteManager to use a service definitions dictionary for cleaner and more maintainable service initialization. Renamed AcceptAllCertificatesSignedWithASpecificKeyPublicKey to AcceptAllCertificatesHandler for clarity. Enhanced Realtime reconnect logic to properly handle cancellation tokens, ensuring pending retry operations are cancelled and disposed when disconnecting.
1 parent 4ebabd7 commit 2a8dd10

File tree

3 files changed

+57
-37
lines changed

3 files changed

+57
-37
lines changed

templates/unity/Assets/Runtime/AppwriteManager.cs.twig

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,19 @@ namespace {{ spec.title | caseUcfirst }}
125125
}
126126
}
127127

128+
private readonly Dictionary<Type, ({{ spec.title | caseUcfirst }}Service flag, Func<Client, Service> factory)> _serviceDefinitions = new()
129+
{
130+
{ typeof(Account), ({{ spec.title | caseUcfirst }}Service.Account, client => new Account(client)) },
131+
{ typeof(Databases), ({{ spec.title | caseUcfirst }}Service.Databases, client => new Databases(client)) },
132+
{ typeof(Functions), ({{ spec.title | caseUcfirst }}Service.Functions, client => new Functions(client)) },
133+
{ typeof(Storage), ({{ spec.title | caseUcfirst }}Service.Storage, client => new Storage(client)) },
134+
{ typeof(Avatars), ({{ spec.title | caseUcfirst }}Service.Avatars, client => new Avatars(client)) },
135+
{ typeof(Graphql), ({{ spec.title | caseUcfirst }}Service.Graphql, client => new Graphql(client)) },
136+
{ typeof(Locale), ({{ spec.title | caseUcfirst }}Service.Locale, client => new Locale(client)) },
137+
{ typeof(Messaging), ({{ spec.title | caseUcfirst }}Service.Messaging, client => new Messaging(client)) },
138+
{ typeof(Teams), ({{ spec.title | caseUcfirst }}Service.Teams, client => new Teams(client)) },
139+
};
140+
128141
/// <summary>
129142
/// Initialize selected {{ spec.title | caseUcfirst }} services based on the configuration.
130143
/// </summary>
@@ -133,50 +146,27 @@ namespace {{ spec.title | caseUcfirst }}
133146
_services.Clear();
134147
var servicesToInit = config.ServicesToInitialize;
135148

136-
// Direct service instantiation - no reflection needed for known service types
137-
// This is more performant and AOT-friendly than generic reflection
138-
139-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Account))
140-
TryCreateService<Account>();
141-
142-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Databases))
143-
TryCreateService<Databases>();
144-
145-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Functions))
146-
TryCreateService<Functions>();
147-
148-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Storage))
149-
TryCreateService<Storage>();
150-
151-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Avatars))
152-
TryCreateService<Avatars>();
153-
154-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Graphql))
155-
TryCreateService<Graphql>();
156-
157-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Locale))
158-
TryCreateService<Locale>();
159-
160-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Messaging))
161-
TryCreateService<Messaging>();
162-
163-
if (servicesToInit.HasFlag({{ spec.title | caseUcfirst }}Service.Teams))
164-
TryCreateService<Teams>();
149+
foreach (var kvp in _serviceDefinitions)
150+
{
151+
if (servicesToInit.HasFlag(kvp.Value.flag))
152+
{
153+
TryCreateService(kvp.Key, kvp.Value.factory);
154+
}
155+
}
165156
}
166157

167158
/// <summary>
168159
/// Try to create and register a service instance.
169160
/// </summary>
170-
private void TryCreateService<T>() where T : Service
161+
private void TryCreateService(Type type, Func<Client, Service> factory)
171162
{
172163
try
173164
{
174-
var service = (T)Activator.CreateInstance(typeof(T), _client);
175-
_services[typeof(T)] = service;
165+
_services[type] = factory(_client);
176166
}
177167
catch (Exception ex)
178168
{
179-
Debug.LogError($"Failed to create service {typeof(T).Name}: {ex.Message}");
169+
Debug.LogError($"Failed to create service {type.Name}: {ex.Message}");
180170
}
181171
}
182172

templates/unity/Assets/Runtime/Core/Client.cs.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ namespace {{ spec.title | caseUcfirst }}
355355
// Handle self-signed certificates
356356
if (_selfSigned)
357357
{
358-
request.certificateHandler = new AcceptAllCertificatesSignedWithASpecificKeyPublicKey();
358+
request.certificateHandler = new AcceptAllCertificatesHandler();
359359
}
360360

361361
return request;
@@ -719,7 +719,7 @@ namespace {{ spec.title | caseUcfirst }}
719719
}
720720

721721
// Custom certificate handler for self-signed certificates
722-
public class AcceptAllCertificatesSignedWithASpecificKeyPublicKey : CertificateHandler
722+
public class AcceptAllCertificatesHandler : CertificateHandler
723723
{
724724
protected override bool ValidateCertificate(byte[] certificateData)
725725
{

templates/unity/Assets/Runtime/Realtime.cs.twig

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,34 @@ namespace {{ spec.title | caseUcfirst }}
429429

430430
Debug.Log($"Reconnecting in {timeout} seconds.");
431431

432+
// Ensure we have a cancellation token source
433+
if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
434+
{
435+
_cancellationTokenSource?.Dispose();
436+
_cancellationTokenSource = new CancellationTokenSource();
437+
}
438+
439+
var token = _cancellationTokenSource.Token;
440+
432441
UniTask.Create(async () =>
433442
{
434-
await UniTask.Delay(TimeSpan.FromSeconds(timeout));
435-
await CreateSocket();
443+
try
444+
{
445+
await UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: token);
446+
447+
// Re-check _reconnect after delay in case disconnect was called during wait
448+
if (!_reconnect || token.IsCancellationRequested)
449+
{
450+
Debug.Log("[Realtime] Retry cancelled - reconnect disabled");
451+
return;
452+
}
453+
454+
await CreateSocket();
455+
}
456+
catch (OperationCanceledException)
457+
{
458+
Debug.Log("[Realtime] Retry cancelled");
459+
}
436460
});
437461
}
438462

@@ -477,7 +501,11 @@ namespace {{ spec.title | caseUcfirst }}
477501
var previousReconnect = _reconnect;
478502
_reconnect = false;
479503
StopHeartbeat();
504+
505+
// Cancel any pending retry operations
480506
_cancellationTokenSource?.Cancel();
507+
_cancellationTokenSource?.Dispose();
508+
_cancellationTokenSource = null;
481509

482510
if (_webSocket != null)
483511
{
@@ -490,6 +518,8 @@ namespace {{ spec.title | caseUcfirst }}
490518
if (allowReconnect)
491519
{
492520
_reconnect = previousReconnect;
521+
// Create fresh cancellation token for future retry operations
522+
_cancellationTokenSource = new CancellationTokenSource();
493523
}
494524
}
495525

0 commit comments

Comments
 (0)