From 9fa74360497ef176ef42f50faf58202d8ecb02a6 Mon Sep 17 00:00:00 2001 From: teach310 Date: Thu, 26 Oct 2023 00:42:23 +0900 Subject: [PATCH 1/2] add NSMutableDictionary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## NSMutableDictionaryが必要な理由 swiftのDictionaryはクラスじゃないから。 https://developer.apple.com/documentation/swift/dictionary Unmanaged.passRetainの引数にするためにはクラスである必要がある。 ## NSMutableDictionaryからswift Dictionaryへの変換 基本的には as でできる。 ただしbool値に関してはNSNumberのため変換後に入るのが数値。 0,1 ではなく false, trueの値が必要な場合には変換用の関数を自前で作る必要がある。 今回はなかった。 --- .../Runtime/Foundation/NSMutableDictionary.cs | 50 +++++++++++++++++ .../Foundation/NSMutableDictionary.cs.meta | 11 ++++ .../Runtime/Foundation/NativeMethods.cs | 9 +++ .../SafeNSMutableDictionaryHandle.cs | 10 ++++ .../SafeNSMutableDictionaryHandle.cs.meta | 11 ++++ .../Foundation/NSMutableDictionaryTests.cs | 55 +++++++++++++++++++ .../NSMutableDictionaryTests.cs.meta | 11 ++++ 7 files changed, 157 insertions(+) create mode 100644 Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs create mode 100644 Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta create mode 100644 Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs create mode 100644 Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta create mode 100644 Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs create mode 100644 Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs new file mode 100644 index 0000000..964761b --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs @@ -0,0 +1,50 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class NSMutableDictionary : IDisposable + { + public SafeNSMutableDictionaryHandle Handle { get; private set; } + + public NSMutableDictionary() + { + Handle = NativeMethods.ns_mutable_dictionary_new(); + } + + public IntPtr GetValue(SafeNSObjectHandle key) + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + if (key.IsInvalid) + throw new ArgumentNullException(nameof(key)); + + return NativeMethods.ns_mutable_dictionary_get_value(Handle, key); + } + + public bool TryGetValue(SafeNSObjectHandle key, out IntPtr value) + { + value = GetValue(key); + return value != IntPtr.Zero; + } + + public void SetValue(SafeNSObjectHandle key, SafeNSObjectHandle value) + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + if (key.IsInvalid) + throw new ArgumentNullException(nameof(key)); + + NativeMethods.ns_mutable_dictionary_set_value(Handle, key, value); + } + + public void SetValue(string key, SafeNSObjectHandle value) + { + using var nsString = new NSString(key); + SetValue(nsString.Handle, value); + } + + public void Dispose() + { + if (Handle != null && !Handle.IsInvalid) + Handle.Dispose(); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta new file mode 100644 index 0000000..9ebbb82 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57ce2fa2a35c04ec1aea132e440957dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs index 4f4e901..9934910 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs @@ -34,5 +34,14 @@ internal static class NativeMethods [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern void ns_string_get_cstring_and_length(SafeNSStringHandle handle, out IntPtr ptr, out int length); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSMutableDictionaryHandle ns_mutable_dictionary_new(); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ns_mutable_dictionary_get_value(SafeNSMutableDictionaryHandle handle, SafeNSObjectHandle key); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern void ns_mutable_dictionary_set_value(SafeNSMutableDictionaryHandle handle, SafeNSObjectHandle key, SafeNSObjectHandle value); } } diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs new file mode 100644 index 0000000..d20eb8d --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs @@ -0,0 +1,10 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class SafeNSMutableDictionaryHandle : SafeNSObjectHandle + { + public SafeNSMutableDictionaryHandle() : base() { } + public SafeNSMutableDictionaryHandle(IntPtr handle) : base(handle) { } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta new file mode 100644 index 0000000..4e25f04 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 109e42256379744fcada5c8f5914169b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs new file mode 100644 index 0000000..a8997d0 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs @@ -0,0 +1,55 @@ +using CoreBluetooth.Foundation; +using NUnit.Framework; + +namespace CoreBluetoothTests.Foundation +{ + public class NSMutableDictionaryTests + { + [Test] + public void New() + { + using var nsDictionary = new NSMutableDictionary(); + Assert.That(nsDictionary.Handle, Is.Not.Null); + Assert.That(nsDictionary.Handle.IsInvalid, Is.False); + } + + [Test] + public void SetAndGetStringValue() + { + using var nsDictionary = new NSMutableDictionary(); + using var key = new NSString("hoge"); + using var value = new NSString("fuga"); + nsDictionary.SetValue(key.Handle, value.Handle); + + using var key2 = new NSString("hoge"); + using var nSString = new NSString(nsDictionary.GetValue(key2.Handle)); + Assert.That(nSString.ToString(), Is.EqualTo("fuga")); + } + + [Test] + public void ReturnZeroPointerIfNotFound() + { + using var nsDictionary = new NSMutableDictionary(); + using var notFoundKey = new NSString("dummy"); + var gotPtr = nsDictionary.GetValue(notFoundKey.Handle); + Assert.That(gotPtr, Is.EqualTo(System.IntPtr.Zero)); + } + + [Test] + public void TryGetValue() + { + using var nsDictionary = new NSMutableDictionary(); + using var key = new NSString("hoge"); + using var value = new NSString("fuga"); + nsDictionary.SetValue(key.Handle, value.Handle); + + using var key2 = new NSString("hoge"); + Assert.That(nsDictionary.TryGetValue(key2.Handle, out var gotPtr), Is.True); + using var nSString = new NSString(gotPtr); + Assert.That(nSString.ToString(), Is.EqualTo("fuga")); + + using var notFoundKey = new NSString("dummy"); + Assert.That(nsDictionary.TryGetValue(notFoundKey.Handle, out gotPtr), Is.False); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta new file mode 100644 index 0000000..a0dbdaf --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3f95fa6f3878490db759d0b3f3b2bd1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From c50392aed13d403430695456297c0fb5eef16c15 Mon Sep 17 00:00:00 2001 From: teach310 Date: Thu, 26 Oct 2023 00:43:02 +0900 Subject: [PATCH 2/2] add plugin ns_mutable_dictionary --- .../FoundationForUnity.swift | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift index 11dc0ad..348db59 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift @@ -52,3 +52,34 @@ public func ns_string_get_cstring_and_length(_ handle: UnsafeRawPointer, _ ptr: length.pointee = 0 } } + +@_cdecl("ns_mutable_dictionary_new") +public func ns_mutable_dictionary_new() -> UnsafeMutableRawPointer { + let instance = NSMutableDictionary() + return Unmanaged.passRetained(instance).toOpaque() +} + +@_cdecl("ns_mutable_dictionary_get_value") +public func ns_mutable_dictionary_get_value(_ handle: UnsafeRawPointer, _ keyPtr: UnsafeRawPointer) -> UnsafeMutableRawPointer? { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + + let key = Unmanaged.fromOpaque(keyPtr).takeUnretainedValue() + if let value = instance[key] as? NSObject { + return Unmanaged.passRetained(value).toOpaque() + } + + return nil +} + +@_cdecl("ns_mutable_dictionary_set_value") +public func ns_mutable_dictionary_set_value(_ handle: UnsafeRawPointer, _ keyPtr: UnsafeRawPointer, _ valuePtr: UnsafeRawPointer?) { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + + let key = Unmanaged.fromOpaque(keyPtr).takeUnretainedValue() + if let valuePtr = valuePtr { + let value = Unmanaged.fromOpaque(valuePtr).takeUnretainedValue() + instance[key] = value + } else { + instance[key] = nil + } +}