mirror of
https://github.com/mii443/AzooKeyKanaKanjiConverter.git
synced 2025-08-22 23:15:25 +00:00
refactor: clean up API
This commit is contained in:
@ -35,14 +35,19 @@ extension Kana2Kanji {
|
|||||||
let indexMap = LatticeDualIndexMap(inputData)
|
let indexMap = LatticeDualIndexMap(inputData)
|
||||||
let latticeIndices = indexMap.indices(inputCount: inputCount, surfaceCount: surfaceCount)
|
let latticeIndices = indexMap.indices(inputCount: inputCount, surfaceCount: surfaceCount)
|
||||||
let rawNodes = latticeIndices.map { index in
|
let rawNodes = latticeIndices.map { index in
|
||||||
|
let inputRange: (startIndex: Int, endIndexRange: Range<Int>?)? = if let iIndex = index.inputIndex {
|
||||||
|
(iIndex, nil)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
let surfaceRange: (startIndex: Int, endIndexRange: Range<Int>?)? = if let sIndex = index.surfaceIndex {
|
let surfaceRange: (startIndex: Int, endIndexRange: Range<Int>?)? = if let sIndex = index.surfaceIndex {
|
||||||
(sIndex, nil)
|
(sIndex, nil)
|
||||||
} else {
|
} else {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
return dicdataStore.getLOUDSDataInRange(
|
return dicdataStore.lookupDicdata(
|
||||||
inputData: inputData,
|
composingText: inputData,
|
||||||
from: index.inputIndex,
|
inputRange: inputRange,
|
||||||
surfaceRange: surfaceRange,
|
surfaceRange: surfaceRange,
|
||||||
needTypoCorrection: needTypoCorrection
|
needTypoCorrection: needTypoCorrection
|
||||||
)
|
)
|
||||||
|
@ -27,14 +27,19 @@ extension Kana2Kanji {
|
|||||||
let indexMap = LatticeDualIndexMap(inputData)
|
let indexMap = LatticeDualIndexMap(inputData)
|
||||||
let latticeIndices = indexMap.indices(inputCount: inputCount, surfaceCount: surfaceCount)
|
let latticeIndices = indexMap.indices(inputCount: inputCount, surfaceCount: surfaceCount)
|
||||||
let rawNodes = latticeIndices.map { index in
|
let rawNodes = latticeIndices.map { index in
|
||||||
|
let inputRange: (startIndex: Int, endIndexRange: Range<Int>?)? = if let iIndex = index.inputIndex {
|
||||||
|
(iIndex, nil)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
let surfaceRange: (startIndex: Int, endIndexRange: Range<Int>?)? = if let sIndex = index.surfaceIndex {
|
let surfaceRange: (startIndex: Int, endIndexRange: Range<Int>?)? = if let sIndex = index.surfaceIndex {
|
||||||
(sIndex, nil)
|
(sIndex, nil)
|
||||||
} else {
|
} else {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
return dicdataStore.getLOUDSDataInRange(
|
return dicdataStore.lookupDicdata(
|
||||||
inputData: inputData,
|
composingText: inputData,
|
||||||
from: index.inputIndex,
|
inputRange: inputRange,
|
||||||
surfaceRange: surfaceRange,
|
surfaceRange: surfaceRange,
|
||||||
needTypoCorrection: false
|
needTypoCorrection: false
|
||||||
)
|
)
|
||||||
|
@ -68,10 +68,9 @@ extension Kana2Kanji {
|
|||||||
} else {
|
} else {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
return self.dicdataStore.getLOUDSDataInRange(
|
return self.dicdataStore.lookupDicdata(
|
||||||
inputData: inputData,
|
composingText: inputData,
|
||||||
from: inputRange?.startIndex,
|
inputRange: inputRange,
|
||||||
toIndexRange: inputRange?.endIndexRange,
|
|
||||||
surfaceRange: surfaceRange,
|
surfaceRange: surfaceRange,
|
||||||
needTypoCorrection: needTypoCorrection
|
needTypoCorrection: needTypoCorrection
|
||||||
)
|
)
|
||||||
|
@ -465,33 +465,33 @@ public final class DicdataStore {
|
|||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// kana2latticeから参照する。
|
/// 辞書データを取得する
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - inputData: 入力データ
|
/// - composingText: 現在の入力情報
|
||||||
/// - from: 起点
|
/// - inputRange: 検索に用いる`composingText.input`の範囲。
|
||||||
/// - toIndexRange: `from ..< (toIndexRange)`の範囲で辞書ルックアップを行う。
|
/// - surfaceRange: 検索に用いる`composingText.convertTarget`の範囲。
|
||||||
public func getLOUDSDataInRange(
|
/// - needTypoCorrection: 誤り訂正を行うかどうか
|
||||||
inputData: ComposingText,
|
/// - Returns: 発見された辞書データを`LatticeNode`のインスタンスとしたもの。
|
||||||
from fromInputIndex: Int?,
|
public func lookupDicdata(
|
||||||
toIndexRange: Range<Int>? = nil,
|
composingText: ComposingText,
|
||||||
|
inputRange:(startIndex: Int, endIndexRange: Range<Int>?)? = nil,
|
||||||
surfaceRange: (startIndex: Int, endIndexRange: Range<Int>?)? = nil,
|
surfaceRange: (startIndex: Int, endIndexRange: Range<Int>?)? = nil,
|
||||||
needTypoCorrection: Bool = true
|
needTypoCorrection: Bool = true
|
||||||
) -> [LatticeNode] {
|
) -> [LatticeNode] {
|
||||||
let inputProcessRange: TypoCorrectionGenerator.ProcessRange?
|
|
||||||
|
|
||||||
// TODO: make `fromInputIndex` optional later.
|
let inputProcessRange: TypoCorrectionGenerator.ProcessRange?
|
||||||
if let fromInputIndex {
|
if let inputRange {
|
||||||
let toInputIndexLeft = toIndexRange?.startIndex ?? fromInputIndex
|
let toInputIndexLeft = inputRange.endIndexRange?.startIndex ?? inputRange.startIndex
|
||||||
let toInputIndexRight = min(
|
let toInputIndexRight = min(
|
||||||
toIndexRange?.endIndex ?? inputData.input.count,
|
inputRange.endIndexRange?.endIndex ?? composingText.input.count,
|
||||||
fromInputIndex + self.maxlength
|
inputRange.startIndex + self.maxlength
|
||||||
)
|
)
|
||||||
if fromInputIndex > toInputIndexLeft || toInputIndexLeft >= toInputIndexRight {
|
if inputRange.startIndex > toInputIndexLeft || toInputIndexLeft >= toInputIndexRight {
|
||||||
debug(#function, "index is wrong")
|
debug(#function, "index is wrong")
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
inputProcessRange = .init(leftIndex: fromInputIndex, rightIndexRange: toInputIndexLeft ..< toInputIndexRight)
|
inputProcessRange = .init(leftIndex: inputRange.startIndex, rightIndexRange: toInputIndexLeft ..< toInputIndexRight)
|
||||||
} else {
|
} else {
|
||||||
inputProcessRange = nil
|
inputProcessRange = nil
|
||||||
}
|
}
|
||||||
@ -500,7 +500,7 @@ public final class DicdataStore {
|
|||||||
if let surfaceRange {
|
if let surfaceRange {
|
||||||
let toSurfaceIndexLeft = surfaceRange.endIndexRange?.startIndex ?? surfaceRange.startIndex
|
let toSurfaceIndexLeft = surfaceRange.endIndexRange?.startIndex ?? surfaceRange.startIndex
|
||||||
let toSurfaceIndexRight = min(
|
let toSurfaceIndexRight = min(
|
||||||
surfaceRange.endIndexRange?.endIndex ?? inputData.convertTarget.count,
|
surfaceRange.endIndexRange?.endIndex ?? composingText.convertTarget.count,
|
||||||
surfaceRange.startIndex + self.maxlength
|
surfaceRange.startIndex + self.maxlength
|
||||||
)
|
)
|
||||||
if surfaceRange.startIndex > toSurfaceIndexLeft || toSurfaceIndexLeft >= toSurfaceIndexRight {
|
if surfaceRange.startIndex > toSurfaceIndexLeft || toSurfaceIndexLeft >= toSurfaceIndexRight {
|
||||||
@ -517,7 +517,7 @@ public final class DicdataStore {
|
|||||||
}
|
}
|
||||||
// MARK: 誤り訂正の対象を列挙する。非常に重い処理。
|
// MARK: 誤り訂正の対象を列挙する。非常に重い処理。
|
||||||
var (stringToInfo, indices, dicdata) = self.movingTowardPrefixSearch(
|
var (stringToInfo, indices, dicdata) = self.movingTowardPrefixSearch(
|
||||||
composingText: inputData,
|
composingText: composingText,
|
||||||
inputProcessRange: inputProcessRange,
|
inputProcessRange: inputProcessRange,
|
||||||
surfaceProcessRange: surfaceProcessRange,
|
surfaceProcessRange: surfaceProcessRange,
|
||||||
useMemory: self.learningManager.enabled,
|
useMemory: self.learningManager.enabled,
|
||||||
@ -544,13 +544,13 @@ public final class DicdataStore {
|
|||||||
|
|
||||||
if let inputProcessRange {
|
if let inputProcessRange {
|
||||||
let segments = (inputProcessRange.leftIndex ..< inputProcessRange.rightIndexRange.endIndex).reduce(into: []) { (segments: inout [String], rightIndex: Int) in
|
let segments = (inputProcessRange.leftIndex ..< inputProcessRange.rightIndexRange.endIndex).reduce(into: []) { (segments: inout [String], rightIndex: Int) in
|
||||||
segments.append((segments.last ?? "") + String(inputData.input[rightIndex].character.toKatakana()))
|
segments.append((segments.last ?? "") + String(composingText.input[rightIndex].character.toKatakana()))
|
||||||
}
|
}
|
||||||
for i in inputProcessRange.rightIndexRange {
|
for i in inputProcessRange.rightIndexRange {
|
||||||
do {
|
do {
|
||||||
let result = self.getWiseDicdata(
|
let result = self.getWiseDicdata(
|
||||||
convertTarget: segments[i - inputProcessRange.leftIndex],
|
convertTarget: segments[i - inputProcessRange.leftIndex],
|
||||||
inputData: inputData,
|
inputData: composingText,
|
||||||
inputRange: inputProcessRange.leftIndex ..< i + 1
|
inputRange: inputProcessRange.leftIndex ..< i + 1
|
||||||
)
|
)
|
||||||
for item in result {
|
for item in result {
|
||||||
@ -560,13 +560,13 @@ public final class DicdataStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let needBOS = fromInputIndex == .zero
|
let needBOS = inputRange?.startIndex == .zero || surfaceRange?.startIndex == .zero
|
||||||
let result: [LatticeNode] = dicdata.compactMap {
|
let result: [LatticeNode] = dicdata.compactMap {
|
||||||
guard let endIndex = stringToInfo[Array($0.ruby)]?.endIndex else {
|
guard let endIndex = stringToInfo[Array($0.ruby)]?.endIndex else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let range: Lattice.LatticeRange = switch endIndex {
|
let range: Lattice.LatticeRange = switch endIndex {
|
||||||
case .input(let endIndex): .input(from: fromInputIndex!, to: endIndex + 1)
|
case .input(let endIndex): .input(from: (inputRange?.startIndex)!, to: endIndex + 1)
|
||||||
case .surface(let endIndex): .surface(from: (surfaceRange?.startIndex)!, to: endIndex + 1)
|
case .surface(let endIndex): .surface(from: (surfaceRange?.startIndex)!, to: endIndex + 1)
|
||||||
}
|
}
|
||||||
let node = LatticeNode(data: $0, range: range)
|
let node = LatticeNode(data: $0, range: range)
|
||||||
|
@ -129,7 +129,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
for (key, word) in mustWords {
|
for (key, word) in mustWords {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition(key, inputStyle: .direct)
|
c.insertAtCursorPosition(key, inputStyle: .direct)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: false)
|
||||||
// 冗長な書き方だが、こうすることで「どの項目でエラーが発生したのか」がはっきりするため、こう書いている。
|
// 冗長な書き方だが、こうすることで「どの項目でエラーが発生したのか」がはっきりするため、こう書いている。
|
||||||
XCTAssertEqual(result.first(where: {$0.data.word == word})?.data.word, word)
|
XCTAssertEqual(result.first(where: {$0.data.word == word})?.data.word, word)
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
for (key, word) in mustWords {
|
for (key, word) in mustWords {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition(key, inputStyle: .direct)
|
c.insertAtCursorPosition(key, inputStyle: .direct)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: false)
|
||||||
XCTAssertNil(result.first(where: {$0.data.word == word && $0.data.ruby == key}))
|
XCTAssertNil(result.first(where: {$0.data.word == word && $0.data.ruby == key}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,17 +170,17 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
for (key, word) in mustWords {
|
for (key, word) in mustWords {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition(key, inputStyle: .direct)
|
c.insertAtCursorPosition(key, inputStyle: .direct)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: true)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: true)
|
||||||
XCTAssertEqual(result.first(where: {$0.data.word == word})?.data.word, word)
|
XCTAssertEqual(result.first(where: {$0.data.word == word})?.data.word, word)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetLOUDSDataInRange() throws {
|
func testLookupDicdata() throws {
|
||||||
let dicdataStore = DicdataStore(convertRequestOptions: requestOptions())
|
let dicdataStore = DicdataStore(convertRequestOptions: requestOptions())
|
||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("ヘンカン", inputStyle: .roman2kana)
|
c.insertAtCursorPosition("ヘンカン", inputStyle: .roman2kana)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: 2..<4)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, 2 ..< 4))
|
||||||
XCTAssertFalse(result.contains(where: {$0.data.word == "変"}))
|
XCTAssertFalse(result.contains(where: {$0.data.word == "変"}))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "変化"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "変化"}))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "変換"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "変換"}))
|
||||||
@ -188,7 +188,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("ヘンカン", inputStyle: .roman2kana)
|
c.insertAtCursorPosition("ヘンカン", inputStyle: .roman2kana)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: 0..<4)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, 0..<4))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "変"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "変"}))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "変化"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "変化"}))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "変換"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "変換"}))
|
||||||
@ -196,19 +196,19 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("ツカッ", inputStyle: .roman2kana)
|
c.insertAtCursorPosition("ツカッ", inputStyle: .roman2kana)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: 2..<3)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, 2..<3))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "使っ"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "使っ"}))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("ツカッt", inputStyle: .roman2kana)
|
c.insertAtCursorPosition("ツカッt", inputStyle: .roman2kana)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: 2..<4)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, 2..<4))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "使っ"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "使っ"}))
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
sequentialInput(&c, sequence: "tukatt", inputStyle: .roman2kana)
|
sequentialInput(&c, sequence: "tukatt", inputStyle: .roman2kana)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: 4..<6)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, 4..<6))
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "使っ"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "使っ"}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,7 +255,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("テストタンゴ", inputStyle: .direct)
|
c.insertAtCursorPosition("テストタンゴ", inputStyle: .direct)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: false)
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "テスト単語"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "テスト単語"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("ドウテキジショ", inputStyle: .direct)
|
c.insertAtCursorPosition("ドウテキジショ", inputStyle: .direct)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: false)
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "動的辞書"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "動的辞書"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
sequentialInput(&c, sequence: "tesutowaーdo", inputStyle: .roman2kana)
|
sequentialInput(&c, sequence: "tesutowaーdo", inputStyle: .roman2kana)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: false)
|
||||||
XCTAssertTrue(result.contains(where: {$0.data.word == "テストワード"}))
|
XCTAssertTrue(result.contains(where: {$0.data.word == "テストワード"}))
|
||||||
XCTAssertEqual(result.first(where: {$0.data.word == "テストワード"})?.range, .input(from: 0, to: 11))
|
XCTAssertEqual(result.first(where: {$0.data.word == "テストワード"})?.range, .input(from: 0, to: 11))
|
||||||
}
|
}
|
||||||
@ -297,7 +297,7 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
do {
|
do {
|
||||||
var c = ComposingText()
|
var c = ComposingText()
|
||||||
c.insertAtCursorPosition("トクシュヨミ", inputStyle: .direct)
|
c.insertAtCursorPosition("トクシュヨミ", inputStyle: .direct)
|
||||||
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
let result = dicdataStore.lookupDicdata(composingText: c, inputRange: (0, c.input.endIndex - 1 ..< c.input.endIndex), needTypoCorrection: false)
|
||||||
let dynamicUserDictResult = result.first(where: {$0.data.word == "特殊読み"})
|
let dynamicUserDictResult = result.first(where: {$0.data.word == "特殊読み"})
|
||||||
XCTAssertNotNil(dynamicUserDictResult)
|
XCTAssertNotNil(dynamicUserDictResult)
|
||||||
XCTAssertEqual(dynamicUserDictResult?.data.metadata, .isFromUserDictionary)
|
XCTAssertEqual(dynamicUserDictResult?.data.metadata, .isFromUserDictionary)
|
||||||
|
Reference in New Issue
Block a user