From 277b8535f20fe07a3fbfcfdeb73836745cbae712 Mon Sep 17 00:00:00 2001
From: Naomi Plasterer <naomi@xmtp.com>
Date: Fri, 23 Feb 2024 08:01:44 -0800
Subject: [PATCH 1/3] add function for deleting database

---
 Sources/XMTPiOS/Client.swift | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Sources/XMTPiOS/Client.swift b/Sources/XMTPiOS/Client.swift
index f74e6945..43f6e2ad 100644
--- a/Sources/XMTPiOS/Client.swift
+++ b/Sources/XMTPiOS/Client.swift
@@ -89,6 +89,7 @@ public final class Client {
 	let apiClient: ApiClient
 	let v3Client: LibXMTP.FfiXmtpClient?
 	public let libXMTPVersion: String = getVersionInfo()
+	let dbPath: String = ""
 
 	/// Access ``Conversations`` for this Client.
 	public lazy var conversations: Conversations = .init(client: self)
@@ -450,6 +451,18 @@ public final class Client {
 	public func subscribe(topics: [Topic]) -> AsyncThrowingStream<Envelope, Error> {
 		return subscribe(topics: topics.map(\.description))
 	}
+	
+	public func deleteLocalDatabase() {
+		let fm = FileManager.default
+		let url = URL(string: dbPath)
+		if (url != nil) {
+			do {
+				try fm.removeItem(at: url!)
+			} catch {
+				print("Error deleting file: \(dbPath)")
+			}
+		}
+	}
 
 	func getUserContact(peerAddress: String) async throws -> ContactBundle? {
 		let peerAddress = EthereumAddress(peerAddress).toChecksumAddress()

From 5f9628bd3fb2296d1eb7911fcedcc2bc28e428c1 Mon Sep 17 00:00:00 2001
From: Ethan Mateja <zombieobject@users.noreply.github.com>
Date: Fri, 23 Feb 2024 09:24:07 -0800
Subject: [PATCH 2/3] Fix Linter Errors

---
 .../XMTPiOSExample/Views/ConversationListView.swift      | 4 +---
 XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift      | 9 ++++-----
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift b/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift
index 2e73f24a..abd9ee2c 100644
--- a/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift
+++ b/XMTPiOSExample/XMTPiOSExample/Views/ConversationListView.swift
@@ -152,12 +152,10 @@ struct ConversationListView: View {
 					print("Error saving \(conversation.topic): \(error)")
 				}
 			case .group:
-				// TODO: handle
+				// Handle this in the future
 				return
 			}
 		}
-
-
 	}
 }
 
diff --git a/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift b/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift
index 9a779009..d56d217f 100644
--- a/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift
+++ b/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift
@@ -6,11 +6,10 @@
 //
 
 import SwiftUI
+import Combine
 import WebKit
 import XMTPiOS
 import WalletConnectRelay
-import Combine
-import SwiftUI
 import WalletConnectModal
 import Starscream
 
@@ -111,7 +110,7 @@ struct LoginView: View {
 	var publishers: [AnyCancellable] = []
 
 	@State private var isShowingWebview = true
-
+    // swiftlint:disable function_body_length
 	init(
 		onConnected: @escaping (Client) -> Void
 	) {
@@ -141,8 +140,8 @@ struct LoginView: View {
 			"eip155": ProposalNamespace(
 				chains: [
 					// swiftlint:disable force_unwrapping
-					Blockchain("eip155:80001")!,        //Polygon Testnet
-					Blockchain("eip155:421613")!        //Arbitrum Testnet
+					Blockchain("eip155:80001")!,        // Polygon Testnet
+					Blockchain("eip155:421613")!        // Arbitrum Testnet
 					// swiftlint:enable force_unwrapping
 				],
 				methods: [

From e9e3190093c3edaaa76250c5881423632411f42a Mon Sep 17 00:00:00 2001
From: Ethan Mateja <zombieobject@users.noreply.github.com>
Date: Fri, 23 Feb 2024 10:05:02 -0800
Subject: [PATCH 3/3] Add Linting to Build Phase + Fix Linting Errors

Also added swiftlint.yml to IOSExample project root in order to pick up rule modifications.
---
 .swiftlint.yml                                | 10 ++++
 Sources/XMTPTestHelpers/TestHelpers.swift     |  4 +-
 Sources/XMTPiOS/Client.swift                  | 17 +++----
 Sources/XMTPiOS/Codecs/AttachmentCodec.swift  |  4 +-
 Sources/XMTPiOS/Codecs/Composite.swift        |  4 +-
 .../Codecs/GroupMembershipChanged.swift       |  1 -
 Sources/XMTPiOS/Codecs/ReactionCodec.swift    | 12 ++---
 Sources/XMTPiOS/Codecs/ReadReceiptCodec.swift |  4 +-
 .../Codecs/RemoteAttachmentCodec.swift        |  6 +--
 Sources/XMTPiOS/Codecs/ReplyCodec.swift       |  4 +-
 Sources/XMTPiOS/Codecs/TextCodec.swift        |  6 +--
 Sources/XMTPiOS/Conversation.swift            |  6 +--
 Sources/XMTPiOS/ConversationV2.swift          |  6 +--
 Sources/XMTPiOS/Group.swift                   | 26 +++++------
 Sources/XMTPiOS/Messages/Invitation.swift     |  2 +-
 Sources/XMTPiOS/Messages/MessageV2.swift      |  4 +-
 Sources/XMTPiOS/Messages/PagingInfo.swift     |  2 +-
 .../XMTPiOS/Messages/PrivateKeyBundle.swift   |  5 +-
 .../XMTPiOS/Messages/PrivateKeyBundleV1.swift |  1 -
 .../XMTPiOS/Messages/PublicKeyBundle.swift    |  2 -
 .../XMTPiOS/Messages/SealedInvitationV1.swift |  1 -
 Sources/XMTPiOS/Messages/SignedContent.swift  |  1 -
 .../XMTPiOS/Messages/SignedPrivateKey.swift   |  1 -
 .../Messages/SignedPublicKeyBundle.swift      |  2 -
 Sources/XMTPiOS/Messages/Token.swift          |  1 -
 Sources/XMTPiOS/Messages/Topic.swift          |  4 +-
 .../XMTPiOS/Messages/UnsignedPublicKey.swift  |  1 -
 Sources/XMTPiOS/SigningKey.swift              |  2 +-
 XMTPiOSExample/.swiftlint.yml                 | 46 +++++++++++++++++++
 .../XMTPiOSExample.xcodeproj/project.pbxproj  | 23 ++++++++++
 .../Account/WalletConnection.swift            |  3 +-
 .../XMTPiOSExample/Views/LoginView.swift      |  1 +
 .../Views/MessageCellView.swift               |  1 -
 .../Views/MessageListView.swift               |  2 +
 34 files changed, 139 insertions(+), 76 deletions(-)
 create mode 100644 XMTPiOSExample/.swiftlint.yml

diff --git a/.swiftlint.yml b/.swiftlint.yml
index e9f40901..d5856a6e 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -11,6 +11,16 @@ disabled_rules: # rule identifiers turned on by default to exclude from running
   - nesting
   - type_body_length
   - file_length
+  - todo
+  - control_statement
+  - switch_case_alignment
+  - empty_enum_arguments
+  - for_where
+  - private_over_fileprivate
+  - redundant_string_enum_value
+  - redundant_optional_initialization
+  - operator_whitespace
+  - comma
 
 opt_in_rules:
   - force_unwrapping
diff --git a/Sources/XMTPTestHelpers/TestHelpers.swift b/Sources/XMTPTestHelpers/TestHelpers.swift
index 9ef4a82e..a6ed38e0 100644
--- a/Sources/XMTPTestHelpers/TestHelpers.swift
+++ b/Sources/XMTPTestHelpers/TestHelpers.swift
@@ -197,7 +197,7 @@ public class FakeApiClient: ApiClient {
 				}
 			}
 		}
- 
+
         if let direction = pagination?.direction {
             switch direction {
             case .ascending:
@@ -244,7 +244,6 @@ public class FakeApiClient: ApiClient {
 		var queryResponse = XMTPiOS.BatchQueryResponse()
         queryResponse.responses = responses
         return queryResponse
-     
     }
 
 	public func query(request: XMTPiOS.QueryRequest) async throws -> XMTPiOS.QueryResponse {
@@ -254,7 +253,6 @@ public class FakeApiClient: ApiClient {
 	public func publish(request: XMTPiOS.PublishRequest) async throws {
         abort() // Not supported on Fake
     }
-
 }
 
 @available(iOS 15, *)
diff --git a/Sources/XMTPiOS/Client.swift b/Sources/XMTPiOS/Client.swift
index 43f6e2ad..eb81fc1e 100644
--- a/Sources/XMTPiOS/Client.swift
+++ b/Sources/XMTPiOS/Client.swift
@@ -133,7 +133,7 @@ public final class Client {
 	) async throws -> FfiXmtpClient? {
 		if options?.mlsAlpha == true, options?.api.env.supportsMLS == true {
 			let dbURL = options?.mlsDbPath ?? URL.documentsDirectory.appendingPathComponent("xmtp-\(options?.api.env.rawValue ?? "")-\(address).db3").path
-			
+
 			var encryptionKey = options?.mlsEncryptionKey
 			if (encryptionKey == nil) {
 				let preferences = UserDefaults.standard
@@ -147,7 +147,7 @@ public final class Client {
 					encryptionKey = preferences.data(forKey: key)
 				}
 			}
-			
+
 			let v3Client = try await LibXMTP.createClient(
 				logger: XMTPLogger(),
 				host: (options?.api.env ?? .local).url,
@@ -169,7 +169,7 @@ public final class Client {
 			} else {
 				try await v3Client.registerIdentity(recoverableWalletSignature: nil)
 			}
-			
+
 			print("LibXMTP \(getVersionInfo())")
 
 			return v3Client
@@ -251,16 +251,15 @@ public final class Client {
 
 		return try await v3Client.canMessage(accountAddresses: [address]) == [true]
 	}
-	
+
 	public func canMessageV3(addresses: [String]) async throws -> Bool {
 		guard let v3Client else {
 			return false
 		}
-		
+
 		return try await !v3Client.canMessage(accountAddresses: addresses).contains(false)
 	}
 
-
 	public static func from(bundle: PrivateKeyBundle, options: ClientOptions? = nil) async throws -> Client {
 		return try await from(v1Bundle: bundle.v1, options: options)
 	}
@@ -281,7 +280,7 @@ public final class Client {
 			privateKeyBundleV1: v1Bundle,
 			signingKey: nil
 		)
-		
+
 		let client = try await LibXMTP.createV2Client(host: options.api.env.url, isSecure: options.api.env.isSecure)
 		let apiClient = try GRPCApiClient(
 			environment: options.api.env,
@@ -451,13 +450,15 @@ public final class Client {
 	public func subscribe(topics: [Topic]) -> AsyncThrowingStream<Envelope, Error> {
 		return subscribe(topics: topics.map(\.description))
 	}
-	
+
 	public func deleteLocalDatabase() {
 		let fm = FileManager.default
 		let url = URL(string: dbPath)
 		if (url != nil) {
 			do {
+				// swiftlint: disable force_unwrapping
 				try fm.removeItem(at: url!)
+				// swiftlint: enable force_unwrapping
 			} catch {
 				print("Error deleting file: \(dbPath)")
 			}
diff --git a/Sources/XMTPiOS/Codecs/AttachmentCodec.swift b/Sources/XMTPiOS/Codecs/AttachmentCodec.swift
index 96c8f843..b8175b3a 100644
--- a/Sources/XMTPiOS/Codecs/AttachmentCodec.swift
+++ b/Sources/XMTPiOS/Codecs/AttachmentCodec.swift
@@ -55,11 +55,11 @@ public struct AttachmentCodec: ContentCodec {
 
 		return attachment
 	}
-    
+
     public func fallback(content: Attachment) throws -> String? {
         return "Can’t display “\(content.filename)”. This app doesn’t support attachments."
     }
-	
+
 	public func shouldPush(content: Attachment) throws -> Bool {
 		return true
 	}
diff --git a/Sources/XMTPiOS/Codecs/Composite.swift b/Sources/XMTPiOS/Codecs/Composite.swift
index 217ef33c..fdb8e518 100644
--- a/Sources/XMTPiOS/Codecs/Composite.swift
+++ b/Sources/XMTPiOS/Codecs/Composite.swift
@@ -41,11 +41,11 @@ struct CompositeCodec: ContentCodec {
 		let decodedComposite = fromComposite(composite: composite)
 		return decodedComposite
 	}
-    
+
     public func fallback(content: DecodedComposite) throws -> String? {
         return nil
     }
-	
+
 	public func shouldPush(content: DecodedComposite) throws -> Bool {
 		return false
 	}
diff --git a/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift b/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift
index 9d59d961..8dcbb039 100644
--- a/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift
+++ b/Sources/XMTPiOS/Codecs/GroupMembershipChanged.swift
@@ -40,4 +40,3 @@ public struct GroupMembershipChangedCodec: ContentCodec {
 		false
 	}
 }
-
diff --git a/Sources/XMTPiOS/Codecs/ReactionCodec.swift b/Sources/XMTPiOS/Codecs/ReactionCodec.swift
index b9ed7033..50fa31c4 100644
--- a/Sources/XMTPiOS/Codecs/ReactionCodec.swift
+++ b/Sources/XMTPiOS/Codecs/ReactionCodec.swift
@@ -7,7 +7,6 @@
 
 import Foundation
 
-
 public let ContentTypeReaction = ContentTypeID(authorityID: "xmtp.org", typeID: "reaction", versionMajor: 1, versionMinor: 0)
 
 public struct Reaction: Codable {
@@ -15,7 +14,7 @@ public struct Reaction: Codable {
     public var action: ReactionAction
     public var content: String
     public var schema: ReactionSchema
-    
+
     public init(reference: String, action: ReactionAction, content: String, schema: ReactionSchema) {
         self.reference = reference
         self.action = action
@@ -26,7 +25,7 @@ public struct Reaction: Codable {
 
 public enum ReactionAction: String, Codable {
     case added, removed, unknown
-    
+
     public init(rawValue: String) {
         switch rawValue {
         case "added":
@@ -41,7 +40,7 @@ public enum ReactionAction: String, Codable {
 
 public enum ReactionSchema: String, Codable {
     case unicode, shortcode, custom, unknown
-    
+
     public init(rawValue: String) {
         switch rawValue {
         case "unicode":
@@ -85,8 +84,9 @@ public struct ReactionCodec: ContentCodec {
             content: String(data: content.content, encoding: .utf8) ?? "",
             schema: ReactionSchema(rawValue: content.parameters["schema"] ?? "")
         )
+		// swiftlint:enable no_optional_try
     }
-    
+
     public func fallback(content: Reaction) throws -> String? {
         switch content.action {
         case .added:
@@ -97,7 +97,7 @@ public struct ReactionCodec: ContentCodec {
             return nil
         }
     }
-	
+
 	public func shouldPush(content: Reaction) throws -> Bool {
 		switch content.action {
 		case .added:
diff --git a/Sources/XMTPiOS/Codecs/ReadReceiptCodec.swift b/Sources/XMTPiOS/Codecs/ReadReceiptCodec.swift
index 164ff23f..c6089665 100644
--- a/Sources/XMTPiOS/Codecs/ReadReceiptCodec.swift
+++ b/Sources/XMTPiOS/Codecs/ReadReceiptCodec.swift
@@ -32,11 +32,11 @@ public struct ReadReceiptCodec: ContentCodec {
     public func decode(content: EncodedContent, client _: Client) throws -> ReadReceipt {
         return ReadReceipt()
     }
-    
+
     public func fallback(content: ReadReceipt) throws -> String? {
         return nil
     }
-	
+
 	public func shouldPush(content: ReadReceipt) throws -> Bool {
 		return false
 	}
diff --git a/Sources/XMTPiOS/Codecs/RemoteAttachmentCodec.swift b/Sources/XMTPiOS/Codecs/RemoteAttachmentCodec.swift
index 32c1b103..19fe5d95 100644
--- a/Sources/XMTPiOS/Codecs/RemoteAttachmentCodec.swift
+++ b/Sources/XMTPiOS/Codecs/RemoteAttachmentCodec.swift
@@ -149,7 +149,7 @@ public struct RemoteAttachment {
 }
 
 public struct RemoteAttachmentCodec: ContentCodec {
-    
+
 	public typealias T = RemoteAttachment
 
 	public init() {}
@@ -207,7 +207,7 @@ public struct RemoteAttachmentCodec: ContentCodec {
 
 		return attachment
 	}
-    
+
     public func fallback(content: RemoteAttachment) throws -> String? {
         return "Can’t display “\(String(describing: content.filename))”. This app doesn’t support attachments."
     }
@@ -223,7 +223,7 @@ public struct RemoteAttachmentCodec: ContentCodec {
 
 		return Data(parameterData)
 	}
-	
+
 	public func shouldPush(content: RemoteAttachment) throws -> Bool {
 		return true
 	}
diff --git a/Sources/XMTPiOS/Codecs/ReplyCodec.swift b/Sources/XMTPiOS/Codecs/ReplyCodec.swift
index b3244813..f6182c9b 100644
--- a/Sources/XMTPiOS/Codecs/ReplyCodec.swift
+++ b/Sources/XMTPiOS/Codecs/ReplyCodec.swift
@@ -62,11 +62,11 @@ public struct ReplyCodec: ContentCodec {
 			throw CodecError.invalidContent
 		}
 	}
-    
+
     public func fallback(content: Reply) throws -> String? {
         return "Replied with “\(content.content)” to an earlier message"
     }
-	
+
 	public func shouldPush(content: Reply) throws -> Bool {
 		return true
 	}
diff --git a/Sources/XMTPiOS/Codecs/TextCodec.swift b/Sources/XMTPiOS/Codecs/TextCodec.swift
index b7f6bf13..dfa1fded 100644
--- a/Sources/XMTPiOS/Codecs/TextCodec.swift
+++ b/Sources/XMTPiOS/Codecs/TextCodec.swift
@@ -14,7 +14,7 @@ enum TextCodecError: Error {
 }
 
 public struct TextCodec: ContentCodec {
-    
+
 	public typealias T = String
 
 	public init() {	}
@@ -42,11 +42,11 @@ public struct TextCodec: ContentCodec {
 			throw TextCodecError.unknownDecodingError
 		}
 	}
-    
+
     public func fallback(content: String) throws -> String? {
         return nil
     }
-	
+
 	public func shouldPush(content: String) throws -> Bool {
 		return true
 	}
diff --git a/Sources/XMTPiOS/Conversation.swift b/Sources/XMTPiOS/Conversation.swift
index 10db65b5..683b04c1 100644
--- a/Sources/XMTPiOS/Conversation.swift
+++ b/Sources/XMTPiOS/Conversation.swift
@@ -89,7 +89,7 @@ public enum Conversation: Sendable {
 			return group.peerAddresses.joined(separator: ",")
 		}
 	}
-	
+
 	public var peerAddresses: [String] {
 		switch self {
 		case let .v1(conversationV1):
@@ -100,7 +100,7 @@ public enum Conversation: Sendable {
 			return group.peerAddresses
 		}
 	}
-	
+
 	public var keyMaterial: Data? {
 		switch self {
 		case let .v1(conversationV1):
@@ -182,7 +182,7 @@ public enum Conversation: Sendable {
 			throw GroupError.notSupportedByGroups
 		}
 	}
-    
+
     public func prepareMessage(encodedContent: EncodedContent, options: SendOptions? = nil) async throws -> PreparedMessage {
         switch self {
         case let .v1(conversationV1):
diff --git a/Sources/XMTPiOS/ConversationV2.swift b/Sources/XMTPiOS/ConversationV2.swift
index 947a64f6..e0379f56 100644
--- a/Sources/XMTPiOS/ConversationV2.swift
+++ b/Sources/XMTPiOS/ConversationV2.swift
@@ -107,7 +107,7 @@ public struct ConversationV2 {
 		}
 
 		var encoded = try encode(codec: codec, content: content)
-        
+
         func fallback<Codec: ContentCodec>(codec: Codec, content: Any) throws -> String? {
             if let content = content as? Codec.T {
                 return try codec.fallback(content: content)
@@ -115,11 +115,11 @@ public struct ConversationV2 {
                 throw CodecError.invalidContent
             }
         }
-        
+
         if let fallback = try fallback(codec: codec, content: content) {
             encoded.fallback = fallback
         }
-		
+
         if let compression = options?.compression {
 			encoded = try encoded.compress(compression)
 		}
diff --git a/Sources/XMTPiOS/Group.swift b/Sources/XMTPiOS/Group.swift
index d7789015..786ae38a 100644
--- a/Sources/XMTPiOS/Group.swift
+++ b/Sources/XMTPiOS/Group.swift
@@ -42,7 +42,7 @@ public struct Group: Identifiable, Equatable, Hashable {
 	public var id: Data {
 		ffiGroup.id()
 	}
-	
+
 	func metadata() throws -> FfiGroupMetadata {
 		return try ffiGroup.groupMetadata()
 	}
@@ -58,11 +58,11 @@ public struct Group: Identifiable, Equatable, Hashable {
 	public func hash(into hasher: inout Hasher) {
 		id.hash(into: &hasher)
 	}
-	
+
 	public func isActive() throws -> Bool {
 		return try ffiGroup.isActive()
 	}
-	
+
 	public func isAdmin() throws -> Bool {
 		return try metadata().creatorAccountAddress().lowercased() == client.address.lowercased()
 	}
@@ -74,7 +74,7 @@ public struct Group: Identifiable, Equatable, Hashable {
 	public func adminAddress() throws -> String {
 		return try metadata().creatorAccountAddress()
 	}
-	
+
 	public var memberAddresses: [String] {
 		do {
 			return try ffiGroup.listMembers().map(\.fromFFI.accountAddress)
@@ -82,7 +82,7 @@ public struct Group: Identifiable, Equatable, Hashable {
 			return []
 		}
 	}
-	
+
 	public var peerAddresses: [String] {
 		var addresses = memberAddresses.map(\.localizedLowercase)
 		if let index = addresses.firstIndex(of: client.address.localizedLowercase) {
@@ -103,17 +103,16 @@ public struct Group: Identifiable, Equatable, Hashable {
 		try await ffiGroup.removeMembers(accountAddresses: addresses)
 	}
 
-
 	public func send<T>(content: T, options: SendOptions? = nil) async throws -> String {
 		let preparedMessage = try await prepareMessage(content: content, options: options)
 		return try await send(encodedContent: preparedMessage)
 	}
-	
+
 	public func send(encodedContent: EncodedContent) async throws -> String {
 		try await ffiGroup.send(contentBytes: encodedContent.serializedData())
 		return id.toHex
 	}
-	
+
 	public func prepareMessage<T>(content: T, options: SendOptions?) async throws -> EncodedContent {
 		let codec = client.codecRegistry.find(for: options?.contentType)
 
@@ -126,7 +125,7 @@ public struct Group: Identifiable, Equatable, Hashable {
 		}
 
 		var encoded = try encode(codec: codec, content: content)
-		
+
 		func fallback<Codec: ContentCodec>(codec: Codec, content: Any) throws -> String? {
 			if let content = content as? Codec.T {
 				return try codec.fallback(content: content)
@@ -134,11 +133,11 @@ public struct Group: Identifiable, Equatable, Hashable {
 				throw CodecError.invalidContent
 			}
 		}
-		
+
 		if let fallback = try fallback(codec: codec, content: content) {
 			encoded.fallback = fallback
 		}
-		
+
 		if let compression = options?.compression {
 			encoded = try encoded.compress(compression)
 		}
@@ -169,7 +168,7 @@ public struct Group: Identifiable, Equatable, Hashable {
 			}
 		}
 	}
-	
+
 	public func streamDecryptedMessages() -> AsyncThrowingStream<DecryptedMessage, Error> {
 		AsyncThrowingStream { continuation in
 			Task.detached {
@@ -216,7 +215,7 @@ public struct Group: Identifiable, Equatable, Hashable {
 			return messages.reversed()
 		}
 	}
-	
+
 	public func decryptedMessages(before: Date? = nil, after: Date? = nil, limit: Int? = nil, direction: PagingInfoSortDirection? = .descending) async throws -> [DecryptedMessage] {
 		var options = FfiListMessagesOptions(sentBeforeNs: nil, sentAfterNs: nil, limit: nil)
 
@@ -242,6 +241,5 @@ public struct Group: Identifiable, Equatable, Hashable {
 		default:
 			return messages.reversed()
 		}
-
 	}
 }
diff --git a/Sources/XMTPiOS/Messages/Invitation.swift b/Sources/XMTPiOS/Messages/Invitation.swift
index c24b03c1..2c156fd1 100644
--- a/Sources/XMTPiOS/Messages/Invitation.swift
+++ b/Sources/XMTPiOS/Messages/Invitation.swift
@@ -16,7 +16,7 @@ extension InvitationV1 {
 		let context = context ?? InvitationV1.Context()
         let myAddress = try sender.toV1().walletAddress
         let theirAddress = try recipient.walletAddress
-        
+
 		let secret = try sender.sharedSecret(
 				peer: recipient,
 				myPreKey: sender.preKeys[0].publicKey,
diff --git a/Sources/XMTPiOS/Messages/MessageV2.swift b/Sources/XMTPiOS/Messages/MessageV2.swift
index c49114bd..0dc64403 100644
--- a/Sources/XMTPiOS/Messages/MessageV2.swift
+++ b/Sources/XMTPiOS/Messages/MessageV2.swift
@@ -98,7 +98,7 @@ extension MessageV2 {
 		let signedBytes = try signedContent.serializedData()
 
 		let ciphertext = try Crypto.encrypt(keyMaterial, signedBytes, additionalData: headerBytes)
-		
+
 		let thirtyDayPeriodsSinceEpoch = Int(date.timeIntervalSince1970 / 60 / 60 / 24 / 30)
 		let info = "\(thirtyDayPeriodsSinceEpoch)-\(client.address)"
 		guard let infoEncoded = info.data(using: .utf8) else {
@@ -106,7 +106,7 @@ extension MessageV2 {
 		}
 
 		let senderHmac = try Crypto.generateHmacSignature(secret: keyMaterial, info: infoEncoded, message: headerBytes)
-		
+
 		let decoded = try codec.decode(content: encodedContent, client: client)
 		let calculatedShouldPush = try codec.shouldPush(content: decoded)
 
diff --git a/Sources/XMTPiOS/Messages/PagingInfo.swift b/Sources/XMTPiOS/Messages/PagingInfo.swift
index 9a19ec1d..2f0f2eb9 100644
--- a/Sources/XMTPiOS/Messages/PagingInfo.swift
+++ b/Sources/XMTPiOS/Messages/PagingInfo.swift
@@ -16,7 +16,7 @@ public struct Pagination {
     public var before: Date?
     public var after: Date?
     public var direction: PagingInfoSortDirection?
-        
+
     public init(limit: Int? = nil, before: Date? = nil, after: Date? = nil, direction: PagingInfoSortDirection? = .descending) {
         self.limit = limit
         self.before = before
diff --git a/Sources/XMTPiOS/Messages/PrivateKeyBundle.swift b/Sources/XMTPiOS/Messages/PrivateKeyBundle.swift
index 35f36d54..dc67a5a2 100644
--- a/Sources/XMTPiOS/Messages/PrivateKeyBundle.swift
+++ b/Sources/XMTPiOS/Messages/PrivateKeyBundle.swift
@@ -7,7 +7,6 @@
 
 import Foundation
 
-
 public typealias PrivateKeyBundle = Xmtp_MessageContents_PrivateKeyBundle
 
 enum PrivateKeyBundleError: Error {
@@ -23,9 +22,9 @@ extension PrivateKeyBundle {
 	func encrypted(with key: SigningKey, preEnableIdentityCallback: PreEventCallback? = nil) async throws -> EncryptedPrivateKeyBundle {
 		let bundleBytes = try serializedData()
 		let walletPreKey = try Crypto.secureRandomBytes(count: 32)
-    
+
 		try await preEnableIdentityCallback?()
-    
+
 		let signature = try await key.sign(message: Signature.enableIdentityText(key: walletPreKey))
 		let cipherText = try Crypto.encrypt(signature.rawDataWithNormalizedRecovery, bundleBytes)
 
diff --git a/Sources/XMTPiOS/Messages/PrivateKeyBundleV1.swift b/Sources/XMTPiOS/Messages/PrivateKeyBundleV1.swift
index cc4627d3..f717ed97 100644
--- a/Sources/XMTPiOS/Messages/PrivateKeyBundleV1.swift
+++ b/Sources/XMTPiOS/Messages/PrivateKeyBundleV1.swift
@@ -9,7 +9,6 @@ import CryptoKit
 import Foundation
 import LibXMTP
 
-
 public typealias PrivateKeyBundleV1 = Xmtp_MessageContents_PrivateKeyBundleV1
 
 extension PrivateKeyBundleV1 {
diff --git a/Sources/XMTPiOS/Messages/PublicKeyBundle.swift b/Sources/XMTPiOS/Messages/PublicKeyBundle.swift
index 23691789..0945db63 100644
--- a/Sources/XMTPiOS/Messages/PublicKeyBundle.swift
+++ b/Sources/XMTPiOS/Messages/PublicKeyBundle.swift
@@ -5,8 +5,6 @@
 //  Created by Pat Nakajima on 11/23/22.
 //
 
-
-
 typealias PublicKeyBundle = Xmtp_MessageContents_PublicKeyBundle
 
 extension PublicKeyBundle {
diff --git a/Sources/XMTPiOS/Messages/SealedInvitationV1.swift b/Sources/XMTPiOS/Messages/SealedInvitationV1.swift
index 054274b0..b1376300 100644
--- a/Sources/XMTPiOS/Messages/SealedInvitationV1.swift
+++ b/Sources/XMTPiOS/Messages/SealedInvitationV1.swift
@@ -7,7 +7,6 @@
 
 import Foundation
 
-
 typealias SealedInvitationV1 = Xmtp_MessageContents_SealedInvitationV1
 
 extension SealedInvitationV1 {
diff --git a/Sources/XMTPiOS/Messages/SignedContent.swift b/Sources/XMTPiOS/Messages/SignedContent.swift
index 42d4e3b8..682cea07 100644
--- a/Sources/XMTPiOS/Messages/SignedContent.swift
+++ b/Sources/XMTPiOS/Messages/SignedContent.swift
@@ -7,7 +7,6 @@
 
 import Foundation
 
-
 typealias SignedContent = Xmtp_MessageContents_SignedContent
 
 extension SignedContent {
diff --git a/Sources/XMTPiOS/Messages/SignedPrivateKey.swift b/Sources/XMTPiOS/Messages/SignedPrivateKey.swift
index f96f414c..17db7255 100644
--- a/Sources/XMTPiOS/Messages/SignedPrivateKey.swift
+++ b/Sources/XMTPiOS/Messages/SignedPrivateKey.swift
@@ -7,7 +7,6 @@
 
 import Foundation
 
-
 public typealias SignedPrivateKey = Xmtp_MessageContents_SignedPrivateKey
 
 extension SignedPrivateKey {
diff --git a/Sources/XMTPiOS/Messages/SignedPublicKeyBundle.swift b/Sources/XMTPiOS/Messages/SignedPublicKeyBundle.swift
index 16758250..4b661697 100644
--- a/Sources/XMTPiOS/Messages/SignedPublicKeyBundle.swift
+++ b/Sources/XMTPiOS/Messages/SignedPublicKeyBundle.swift
@@ -5,8 +5,6 @@
 //  Created by Pat Nakajima on 11/23/22.
 //
 
-
-
 public typealias SignedPublicKeyBundle = Xmtp_MessageContents_SignedPublicKeyBundle
 
 extension SignedPublicKeyBundle {
diff --git a/Sources/XMTPiOS/Messages/Token.swift b/Sources/XMTPiOS/Messages/Token.swift
index 5165e156..ec95aaa1 100644
--- a/Sources/XMTPiOS/Messages/Token.swift
+++ b/Sources/XMTPiOS/Messages/Token.swift
@@ -7,5 +7,4 @@
 
 import Foundation
 
-
 typealias Token = Xmtp_MessageApi_V1_Token
diff --git a/Sources/XMTPiOS/Messages/Topic.swift b/Sources/XMTPiOS/Messages/Topic.swift
index 61b8135e..a3942d1f 100644
--- a/Sources/XMTPiOS/Messages/Topic.swift
+++ b/Sources/XMTPiOS/Messages/Topic.swift
@@ -5,8 +5,6 @@
 //  Created by Pat Nakajima on 11/17/22.
 //
 
-
-
 public enum Topic {
 	case userPrivateStoreKeyBundle(String),
 	     contact(String),
@@ -39,7 +37,7 @@ public enum Topic {
 	private func wrap(_ value: String) -> String {
 		"/xmtp/0/\(value)/proto"
 	}
-    
+
     static func isValidTopic(topic: String) -> Bool {
         return topic.allSatisfy(\.isASCII)
     }
diff --git a/Sources/XMTPiOS/Messages/UnsignedPublicKey.swift b/Sources/XMTPiOS/Messages/UnsignedPublicKey.swift
index 69e26afd..17fa3ee6 100644
--- a/Sources/XMTPiOS/Messages/UnsignedPublicKey.swift
+++ b/Sources/XMTPiOS/Messages/UnsignedPublicKey.swift
@@ -7,7 +7,6 @@
 
 import Foundation
 
-
 typealias UnsignedPublicKey = Xmtp_MessageContents_UnsignedPublicKey
 
 extension UnsignedPublicKey {
diff --git a/Sources/XMTPiOS/SigningKey.swift b/Sources/XMTPiOS/SigningKey.swift
index 3f5673e1..7eb93eab 100644
--- a/Sources/XMTPiOS/SigningKey.swift
+++ b/Sources/XMTPiOS/SigningKey.swift
@@ -35,7 +35,7 @@ extension SigningKey {
 		slimKey.secp256K1Uncompressed = identity.publicKey.secp256K1Uncompressed
 
 		try await preCreateIdentityCallback?()
-    
+
 		let signatureText = Signature.createIdentityText(key: try slimKey.serializedData())
 		let signature = try await sign(message: signatureText)
 
diff --git a/XMTPiOSExample/.swiftlint.yml b/XMTPiOSExample/.swiftlint.yml
new file mode 100644
index 00000000..d5856a6e
--- /dev/null
+++ b/XMTPiOSExample/.swiftlint.yml
@@ -0,0 +1,46 @@
+excluded:
+  - Sources/XMTP/Proto
+
+disabled_rules: # rule identifiers turned on by default to exclude from running
+  - type_name
+  - identifier_name
+  - multiple_closures_with_trailing_closure
+  - cyclomatic_complexity
+  - trailing_comma
+  - opening_brace
+  - nesting
+  - type_body_length
+  - file_length
+  - todo
+  - control_statement
+  - switch_case_alignment
+  - empty_enum_arguments
+  - for_where
+  - private_over_fileprivate
+  - redundant_string_enum_value
+  - redundant_optional_initialization
+  - operator_whitespace
+  - comma
+
+opt_in_rules:
+  - force_unwrapping
+
+force_unwrapping:
+  severity: error
+
+line_length: 500
+
+custom_rules:
+  no_optional_try: # rule identifier
+    included:
+      - ".*\\.swift" # regex that defines paths to include during linting. optional.
+    excluded:
+      - ".*Tests?\\.swift" # regex that defines paths to exclude during linting. optional
+    name: "No optional try" # rule name. optional.
+    regex: try[\\?]
+    capture_group: 0 # number of regex capture group to highlight the rule violation at. optional.
+    message: "Handle this error instead of doing this.." # violation message. optional.
+    severity: error # violation severity. optional.
+    #match_kinds:# SyntaxKinds to match. optional.
+    #   - comment
+    # - identifier
diff --git a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj
index a699a4fa..bbfa15d8 100644
--- a/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj
+++ b/XMTPiOSExample/XMTPiOSExample.xcodeproj/project.pbxproj
@@ -238,6 +238,7 @@
 				A628198B292DC825004B9117 /* Sources */,
 				A628198C292DC825004B9117 /* Frameworks */,
 				A628198D292DC825004B9117 /* Resources */,
+				636E6B4E2B890E7300B43B62 /* Code Linter: SwiftLint */,
 				A65F0701297B5BCC00C3C76E /* Embed Foundation Extensions */,
 			);
 			buildRules = (
@@ -344,6 +345,28 @@
 		};
 /* End PBXResourcesBuildPhase section */
 
+/* Begin PBXShellScriptBuildPhase section */
+		636E6B4E2B890E7300B43B62 /* Code Linter: SwiftLint */ = {
+			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+			);
+			name = "Code Linter: SwiftLint";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n  if which swiftlint >/dev/null; then\n    swiftlint lint --strict\n  else \n    echo \"warning: SwiftLint not installed, install it via Homebrew according to README instructions\"\n  fi\nfi\n\n";
+		};
+/* End PBXShellScriptBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
 		A628198B292DC825004B9117 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
diff --git a/XMTPiOSExample/XMTPiOSExample/Account/WalletConnection.swift b/XMTPiOSExample/XMTPiOSExample/Account/WalletConnection.swift
index 2c6461c7..5666ef8c 100644
--- a/XMTPiOSExample/XMTPiOSExample/Account/WalletConnection.swift
+++ b/XMTPiOSExample/XMTPiOSExample/Account/WalletConnection.swift
@@ -10,7 +10,6 @@ import UIKit
 import WalletConnectSwift
 import web3
 import XMTPiOS
-import UIKit
 
 extension WCURL {
 	var asURL: URL {
@@ -183,7 +182,7 @@ class WCWalletConnection: WalletConnection, WalletConnectSwift.ClientDelegate {
 	func client(_: WalletConnectSwift.Client, didFailToConnect _: WalletConnectSwift.WCURL) {}
 
 	func client(_: WalletConnectSwift.Client, didConnect session: WalletConnectSwift.Session) {
-		// TODO: Cache session
+		// Future Implementation Cache session
 		self.session = session
 	}
 
diff --git a/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift b/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift
index d56d217f..aa1bf911 100644
--- a/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift
+++ b/XMTPiOSExample/XMTPiOSExample/Views/LoginView.swift
@@ -181,6 +181,7 @@ struct LoginView: View {
 			}
 			.store(in: &publishers)
 	}
+    // swiftlint:enable function_body_length
 
 	var body: some View {
 		ModalWrapper()
diff --git a/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift b/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift
index ee21cde9..94c1fcae 100644
--- a/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift
+++ b/XMTPiOSExample/XMTPiOSExample/Views/MessageCellView.swift
@@ -60,7 +60,6 @@ struct MessageTextView: View {
 		} catch {
 			return message.fallbackContent
 		}
-		// swiftlint:enable force_try
 	}
 
 	var background: Color {
diff --git a/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift b/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift
index 0c6e5aec..087035ec 100644
--- a/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift
+++ b/XMTPiOSExample/XMTPiOSExample/Views/MessageListView.swift
@@ -43,6 +43,7 @@ struct MessageListView: View {
 struct MessageListView_Previews: PreviewProvider {
 	static var previews: some View {
 		PreviewClientProvider { client in
+            // swiftlint: disable comma
 			MessageListView(
 				myAddress: "0x00", messages: [
 					DecodedMessage.preview(client: client, topic: "foo", body: "Hello", senderAddress: "0x00", sent: Date().addingTimeInterval(-10)),
@@ -53,6 +54,7 @@ struct MessageListView_Previews: PreviewProvider {
 					DecodedMessage.preview(client: client, topic: "foo",body: "🧐", senderAddress: "0x00", sent: Date().addingTimeInterval(-5)),
 				]
 			)
+            // swiftlint: enable comma
 		}
 	}
 }