diff --git a/Sources/XXClient/Models/Contact.swift b/Sources/XXClient/Models/Contact.swift
index 03e08f2a517d98571ee0ad9efc497c9ce930460d..6bc9b769841482eadf0f7544869769361254c860 100644
--- a/Sources/XXClient/Models/Contact.swift
+++ b/Sources/XXClient/Models/Contact.swift
@@ -44,6 +44,18 @@ extension Contact: Equatable {
   }
 }
 
+extension Contact {
+  public func getFact(_ type: FactType) throws -> Fact? {
+    try getFacts().get(type)
+  }
+
+  public mutating func setFact(_ type: FactType, _ value: String?) throws {
+    var facts = try getFacts()
+    facts.set(type, value)
+    try setFacts(facts)
+  }
+}
+
 extension Contact {
   public static func live(
     _ data: Data,
diff --git a/Sources/XXClient/Models/Fact.swift b/Sources/XXClient/Models/Fact.swift
index 2ee8a6b3a87720869aafc10e4c746ebcc2565c55..8b2de9f79e5aaa88bb6eb3f3e94ae592c6d50930 100644
--- a/Sources/XXClient/Models/Fact.swift
+++ b/Sources/XXClient/Models/Fact.swift
@@ -2,21 +2,21 @@ import Foundation
 
 public struct Fact: Equatable {
   public init(
-    fact: String,
-    type: Int
+    type: FactType,
+    value: String
   ) {
-    self.fact = fact
     self.type = type
+    self.value = value
   }
 
-  public var fact: String
-  public var type: Int
+  public var type: FactType
+  public var value: String
 }
 
 extension Fact: Codable {
   enum CodingKeys: String, CodingKey {
-    case fact = "Fact"
     case type = "T"
+    case value = "Fact"
   }
 
   public static func decode(_ data: Data) throws -> Self {
@@ -28,6 +28,21 @@ extension Fact: Codable {
   }
 }
 
+extension Fact {
+  @available(iOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.init(type:value:)`.")
+  @available(macOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.init(type:value:)`.")
+  public init(fact: String, type: Int) {
+    self.init(type: .init(rawValue: type), value: fact)
+  }
+
+  @available(iOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.value`.")
+  @available(macOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.value`.")
+  public var fact: String {
+    get { value }
+    set { value = newValue }
+  }
+}
+
 extension Array where Element == Fact {
   public static func decode(_ data: Data) throws -> Self {
     if let string = String(data: data, encoding: .utf8), string == "null" {
@@ -43,3 +58,17 @@ extension Array where Element == Fact {
     return try JSONEncoder().encode(self)
   }
 }
+
+extension Array where Element == Fact {
+  public func get(_ type: FactType) -> Fact? {
+    first(where: { $0.type == type })
+  }
+
+  public mutating func set(_ type: FactType, _ value: String?) {
+    removeAll(where: { $0.type == type })
+    if let value = value {
+      append(Fact(type: type, value: value))
+      sort(by: { $0.type < $1.type })
+    }
+  }
+}
diff --git a/Sources/XXClient/Models/FactType.swift b/Sources/XXClient/Models/FactType.swift
new file mode 100644
index 0000000000000000000000000000000000000000..cbae1a7ebdeaf510440dfcffb631848f3fbe4353
--- /dev/null
+++ b/Sources/XXClient/Models/FactType.swift
@@ -0,0 +1,49 @@
+import Foundation
+
+public enum FactType: Equatable {
+  public static let knownTypes: [FactType] = [.username, .email, .phone]
+
+  case username
+  case email
+  case phone
+  case other(Int)
+}
+
+extension FactType: RawRepresentable {
+  public init(rawValue: Int) {
+    if let known = FactType.knownTypes.first(where: { $0.rawValue == rawValue }) {
+      self = known
+    } else {
+      self = .other(rawValue)
+    }
+  }
+
+  public var rawValue: Int {
+    switch self {
+    case .username: return 0
+    case .email: return 1
+    case .phone: return 2
+    case .other(let rawValue): return rawValue
+    }
+  }
+}
+
+extension FactType: ExpressibleByIntegerLiteral {
+  public init(integerLiteral value: IntegerLiteralType) {
+    self.init(rawValue: value)
+  }
+}
+
+extension FactType: Comparable {}
+
+extension FactType: Codable {
+  public init(from decoder: Decoder) throws {
+    let container = try decoder.singleValueContainer()
+    self.init(rawValue: try container.decode(Int.self))
+  }
+
+  public func encode(to encoder: Encoder) throws {
+    var container = encoder.singleValueContainer()
+    try container.encode(rawValue)
+  }
+}
diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift
index a24a7652de827130a01f145c7df99dcdf7d172dd..5acd76d7c9b05d2c4eee1c01effdbefbcd4be3a7 100644
--- a/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift
@@ -74,13 +74,13 @@ extension MessengerSearchUsers.Query {
   var facts: [Fact] {
     var facts: [Fact] = []
     if let username = username, username.isEmpty == false {
-      facts.append(Fact(fact: username, type: 0))
+      facts.set(.username, username)
     }
     if let email = email, email.isEmpty == false {
-      facts.append(Fact(fact: email, type: 1))
+      facts.set(.email, email)
     }
     if let phone = phone, phone.isEmpty == false {
-      facts.append(Fact(fact: phone, type: 2))
+      facts.set(.phone, phone)
     }
     return facts
   }
diff --git a/Tests/XXClientTests/Models/ContactTests.swift b/Tests/XXClientTests/Models/ContactTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..91dc41491650e15caa5b19bbbcd4b03ed26b37c4
--- /dev/null
+++ b/Tests/XXClientTests/Models/ContactTests.swift
@@ -0,0 +1,61 @@
+import CustomDump
+import XCTest
+@testable import XXClient
+
+final class ContactTests: XCTestCase {
+  func testGetFact() throws {
+    var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
+    contact.getFactsFromContact.run = { _ in
+      [
+        Fact(type: .username, value: "username"),
+        Fact(type: .email, value: "email"),
+        Fact(type: .phone, value: "phone"),
+        Fact(type: .other(3), value: "other"),
+      ]
+    }
+
+    XCTAssertNoDifference(
+      [
+        try contact.getFact(.username),
+        try contact.getFact(.email),
+        try contact.getFact(.phone),
+        try contact.getFact(.other(3)),
+        try contact.getFact(.other(4)),
+      ],
+      [
+        Fact(type: .username, value: "username"),
+        Fact(type: .email, value: "email"),
+        Fact(type: .phone, value: "phone"),
+        Fact(type: .other(3), value: "other"),
+        nil
+      ]
+    )
+  }
+
+  func testSetFact() throws {
+    var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
+    var facts: [Fact] = [
+      Fact(type: .username, value: "username"),
+      Fact(type: .email, value: "email"),
+      Fact(type: .phone, value: "phone"),
+      Fact(type: .other(3), value: "other-3"),
+    ]
+    contact.getFactsFromContact.run = { _ in facts }
+    contact.setFactsOnContact.run = { data, newFacts in
+      facts = newFacts
+      return data
+    }
+
+    try contact.setFact(.username, "new-username")
+    try contact.setFact(.other(4), "new-other-4")
+    try contact.setFact(.other(3), "new-other-3")
+    try contact.setFact(.email, nil)
+
+    XCTAssertNoDifference(facts, [
+      Fact(type: .username, value: "new-username"),
+      Fact(type: .phone, value: "phone"),
+      Fact(type: .other(3), value: "new-other-3"),
+      Fact(type: .other(4), value: "new-other-4"),
+    ])
+  }
+}
diff --git a/Tests/XXClientTests/Models/FactTests.swift b/Tests/XXClientTests/Models/FactTests.swift
index c844af66f73b6e4686e51436f49d8a28a1c79568..38b6c560f73b049fa7ac33b7f7a4716cc02eee18 100644
--- a/Tests/XXClientTests/Models/FactTests.swift
+++ b/Tests/XXClientTests/Models/FactTests.swift
@@ -5,7 +5,7 @@ import XCTest
 final class FactTests: XCTestCase {
   func testCoding() throws {
     let factValue = "Zezima"
-    let factType: Int = 0
+    let factType: Int = 123
     let jsonString = """
     {
       "Fact": "\(factValue)",
@@ -16,8 +16,8 @@ final class FactTests: XCTestCase {
     let model = try Fact.decode(jsonData)
 
     XCTAssertNoDifference(model, Fact(
-      fact: factValue,
-      type: factType
+      type: .other(123),
+      value: factValue
     ))
 
     let encodedModel = try model.encode()
@@ -28,9 +28,9 @@ final class FactTests: XCTestCase {
 
   func testCodingArray() throws {
     let models = [
-      Fact(fact: "abcd", type: 0),
-      Fact(fact: "efgh", type: 1),
-      Fact(fact: "ijkl", type: 2),
+      Fact(type: .username, value: "abcd"),
+      Fact(type: .email, value: "efgh"),
+      Fact(type: .phone, value: "ijkl"),
     ]
 
     let encodedModels = try models.encode()
@@ -51,4 +51,49 @@ final class FactTests: XCTestCase {
 
     XCTAssertNoDifference(encodedModels, jsonData)
   }
+
+  func testArrayGetter() {
+    let facts = [
+      Fact(type: .username, value: "username"),
+      Fact(type: .email, value: "email"),
+      Fact(type: .phone, value: "phone"),
+      Fact(type: .other(3), value: "other"),
+    ]
+
+    XCTAssertNoDifference(
+      [
+        facts.get(.username),
+        facts.get(.email),
+        facts.get(.phone),
+        facts.get(.other(3)),
+        facts.get(.other(4)),
+      ],
+      [
+        Fact(type: .username, value: "username"),
+        Fact(type: .email, value: "email"),
+        Fact(type: .phone, value: "phone"),
+        Fact(type: .other(3), value: "other"),
+        nil
+      ]
+    )
+  }
+
+  func testArraySetter() {
+    var facts: [Fact] = []
+
+    facts.set(.email, "email")
+    facts.set(.phone, "phone")
+    facts.set(.other(3), "other")
+    facts.set(.username, "username")
+
+    XCTAssertNoDifference(
+      facts,
+      [
+        Fact(type: .username, value: "username"),
+        Fact(type: .email, value: "email"),
+        Fact(type: .phone, value: "phone"),
+        Fact(type: .other(3), value: "other"),
+      ]
+    )
+  }
 }
diff --git a/Tests/XXClientTests/Models/FactTypeTests.swift b/Tests/XXClientTests/Models/FactTypeTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f8c3347be43605cff8d2b240ae9d3bcbcd4631ed
--- /dev/null
+++ b/Tests/XXClientTests/Models/FactTypeTests.swift
@@ -0,0 +1,44 @@
+import CustomDump
+import Foundation
+import XCTest
+@testable import XXClient
+
+final class FactTypeTests: XCTestCase {
+  func testDecoding() throws {
+    let decoder = Foundation.JSONDecoder()
+
+    XCTAssertNoDifference(
+      [
+        try decoder.decode(FactType.self, from: "0".data(using: .utf8)!),
+        try decoder.decode(FactType.self, from: "1".data(using: .utf8)!),
+        try decoder.decode(FactType.self, from: "2".data(using: .utf8)!),
+        try decoder.decode(FactType.self, from: "3".data(using: .utf8)!),
+      ],
+      [
+        FactType.username,
+        FactType.email,
+        FactType.phone,
+        FactType.other(3),
+      ]
+    )
+  }
+
+  func testEncoding() throws {
+    let encoder = Foundation.JSONEncoder()
+
+    XCTAssertNoDifference(
+      [
+        try encoder.encode(FactType.username),
+        try encoder.encode(FactType.email),
+        try encoder.encode(FactType.phone),
+        try encoder.encode(FactType.other(3)),
+      ],
+      [
+        "0".data(using: .utf8)!,
+        "1".data(using: .utf8)!,
+        "2".data(using: .utf8)!,
+        "3".data(using: .utf8)!,
+      ]
+    )
+  }
+}
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift
index b6eaac39a1d79cfbf665e7b9a8dc90fab2d161af..9ffa1c82bdc0fec3fc7b089c2ef25a60f634d86d 100644
--- a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift
@@ -181,9 +181,9 @@ final class MessengerSearchUsersTests: XCTestCase {
         phone: "phone"
       ).facts,
       [
-        Fact(fact: "username", type: 0),
-        Fact(fact: "email", type: 1),
-        Fact(fact: "phone", type: 2),
+        Fact(type: .username, value: "username"),
+        Fact(type: .email, value: "email"),
+        Fact(type: .phone, value: "phone"),
       ]
     )
 
@@ -194,7 +194,7 @@ final class MessengerSearchUsersTests: XCTestCase {
         phone: nil
       ).facts,
       [
-        Fact(fact: "username", type: 0),
+        Fact(type: .username, value: "username"),
       ]
     )
   }