From 53296d3fc1240add45d8302a56990735a4977a46 Mon Sep 17 00:00:00 2001 From: "n.kaberov" Date: Sun, 1 Sep 2019 21:44:18 +0300 Subject: [PATCH] Version 3.27.1 --- Documentation/FAQ methods documentation.md | 418 ++++++++++++++++++ Example/Tests/SQLiteHistoryStorageTests.swift | 4 +- .../project.pbxproj | 4 + Info.plist | 2 +- README.md | 7 +- WebimClientLibrary.podspec | 2 +- .../Backend/AbstractRequestLoop.swift | 2 +- .../Backend/DeltaCallback.swift | 18 +- .../Backend/SQLiteHistoryStorage.swift | 3 +- .../Backend/Utilities/DepartmentFactory.swift | 2 +- .../Backend/Utilities/InternalUtils.swift | 2 +- .../Backend/Utilities/OperatorFactory.swift | 7 +- .../Implementation/WebimSessionImpl.swift | 23 +- 13 files changed, 467 insertions(+), 27 deletions(-) create mode 100644 Documentation/FAQ methods documentation.md diff --git a/Documentation/FAQ methods documentation.md b/Documentation/FAQ methods documentation.md new file mode 100644 index 00000000..cee9fc1f --- /dev/null +++ b/Documentation/FAQ methods documentation.md @@ -0,0 +1,418 @@ +

С чего начать.

+Для работы с блоком часто задаваемых вопросов (FAQ), необходимо вызвать метод Webim.newFAQBuilder() для получения экземпляра класса FAQBuilder. С помощью метода set(accountName:) необходимо установить имя аккаунта. Метод faqBuilder.build() вернёт экземпляр класса FAQ. После вызова метода faq.resume() можно начинать работать с получением необходимых структур. + + +

метод newFAQBuilder() класса Webim

+Метод, необходимый для получения объекта FAQBuilder, который в свою очередь необходим для создания экземпляра класса FAQ. + + +

класс FAQBuilder

+Класс, экземпляр которого используется для получения экземпляра класса FAQSession. Экземпляр класса получается методом newFAQBuilder() класса Webim. +

метод экземпляра set(accountName:)

+Метод необходим для задания названия аккаунта в системе Webim при создании блока часто задаваемых вопросов. + +Параметр accountName – название аккаунта клиента в системе Webim. Обычно представляет из себя URL-сервера (например, https://demo.webim.ru), но может иметь и вид названия аккаунта одним словом (например, «demo»), если сервер находится в домене webim.ru. Тип параметра – String. + +Возвращает тот же экземпляр класса FAQBuilder, но с установленным названием аккаунта. + +Для получения экземпляра FAQ вызов метода является обязательным. + +

метод экземпляра build()

+Метод, который вызывается в заключение перечисленных выше методов для получения экземпляра FAQ. + +Возвращает объект FAQ с установленными параметрами с помощью остальных методов экземпляра класса. + +Может выбрасывать ошибки типа FAQBuilderError. + +Требует вызова методов set(accountName:). Также предварительно могут быть вызваны и любые другие (в любом порядке и сочетании) методы экземпляра класса FAQBuilder. + + +

перечисляемый тип FAQBuilderError

+Значение типа соответствуют возможным ошибкам, которые могут возникнуть при вызове метода build() класса FAQBuilder. + +

случай NIL_ACCOUNT_NAME

+Возникает, когда при создании блока часто задаваемых вопросов не было передано значение названия аккаунта (или это значение nil). + +

протокол FAQ

+Протокол, позволяющий производить манипуляции с текущим списком часто задаваемых вопросов. + +Экземпляр класса FAQ получается с помощью методов класса FAQBuilder. + +

метод экземпляра resume()

+При создании экземпляра класса FAQ, соответствующий ему список часто задаваемых вопросов находится в приостановленном состоянии. Данный метод необходим для того, чтобы стартовать сетевую активность. + +Может выбрасывать ошибки типа FAQAccessError. + +Необходим для полноценного функционирования сервиса в контексте приложения. + + +

метод экземпляра pause()

+Метод, который используется для приостановки сетевой активности. Если список часто задаваемых вопросов уже находится в приостановленном состоянии, метод не производит никаких действий. + +Может выбрасывать ошибки типа FAQAccessError. + +Для вызова метода список часто задаваемых вопросов не должен находиться в деактивированном состоянии. + + +

метод экземпляра destroy()

+ +Метод, который используется для деактивации списка часто задаваемых вопросов и экземпляра класса. После вызова данного метода, никакие методы использованы быть не могут. + +Может выбрасывать ошибки типа FAQAccessError. + + +

метод экземпляра getCategory(id:completion:)

+Метод, который используется для получения объекта класса FAQCategory через completion. Если запрос завершился с ошибкой, то полученная категория будет равна nil. + +Параметр id – ID категории блока часто задаваемых вопросов. Тип – Int. + +Параметр completion – замыкание, которое выполняется после завершения выполнения метода. Замыкание содержит один параметр типа FAQCategory и не имеет возвращаемого типа. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + +

метод экземпляра getCachedCategory(id:completion:)

+Метод, который используется для получения объекта класса FAQCategory через completion из сохранённого кэша. Если запрос завершился с ошибкой, то полученная категория будет равна nil. + +Параметр id – ID категории блока часто задаваемых вопросов. Тип – Int. + +Параметр completion – замыкание, которое выполняется после завершения выполнения метода. Замыкание содержит один параметр типа FAQCategory и не имеет возвращаемого типа. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + +

метод экземпляра getItem(id:completion:)

+Метод, который используется для получения объекта класса FAQItem через completion. Если запрос завершился с ошибкой, то полученная страница будет равна nil. + +Параметр id – ID страница блока часто задаваемых вопросов. Тип – String. + +Параметр completion – замыкание, которое выполняется после завершения выполнения метода. Замыкание содержит один параметр типа FAQItem и не имеет возвращаемого типа. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + +

метод экземпляра getStructure(id:completion:)

+Метод, который используется для получения объекта класса FAQStructure через completion. Если запрос завершился с ошибкой, то полученное дерево (структура) будет равно nil. + +Параметр id – ID вершины, которая будет корнем дерева (структуры) блока часто задаваемых вопросов. Тип – Int. + +Параметр completion – замыкание, которое выполняется после завершения выполнения метода. Замыкание содержит один параметр типа FAQStructure и не имеет возвращаемого типа. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + +

метод экземпляра like(item:)

+Метод, который позволяет поставить отметку "нравится" для статьи. + +Параметр item – страница, которой необходимо поставить оценку. Тип – FAQItem. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + +

метод экземпляра dislike(item:)

+Метод, который позволяет поставить отметку "не нравится" для статьи. + +Параметр item – страница, которой необходимо поставить оценку. Тип – FAQItem. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + +

метод экземпляра search(query:category:limitOfItems:completion:)

+Метод, который позволяет найти статьи по ключевому слову. + +Параметр query – запрос, по которому осуществляется поиск. Тип – String. + +Параметр category – id категории, в которой осуществляется поиск. Тип – Int. + +Параметр limitOfItems – число, не больше которого нужно вернуть статей. + +Параметр completion – замыкание, которое выполняется после завершения выполнения метода. Замыкание содержит один параметр типа массив FAQSearchItem и не имеет возвращаемого типа. + +Ничего не возвращает. + +Может выбрасывать ошибки типа FAQAccessError + +Для правильной работы метода требуется вызов метод resume(). + + +

перечисляемый тип FAQAccessError

+Значение типа соответствуют ошибкам, которые могут выбрасываться некоторыми методами протоколов FAQ. + +

случай INVALID_THREAD

+Возникает, когда метод был вызван не из потока, в котором был создан экземпляр блока часто задаваемых вопросов. + +

случай INVALID_SESSION

+Возникает при попытке использования методов недействительного экземпляра FAQ (например, после того, как на нем был вызван метод destroy()). + +

протокол FAQCategory

+Представление категории блока часто задаваемых вопросов. Предоставляет методы получения информации о категории. + +Объекты FAQCategory могут быть получены с помощью completion в методе getCategory(id:completion) + +

метод getId()

+Не принимает параметров. + +Возвращает идентификатор категории. Тип – Int. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getTitle()

+Не принимает параметров. + +Возвращает заголовок категории. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getItems()

+Категория содержит страницы блока часто задаваемых вопросов. + +Не принимает параметров. + +Возвращает страницы, принадлежащих категории. Тип – [FAQItem]. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getSubcategories()

+Категория содержит подкатегории. Для удобства данный метод возвращает информацию о подкатегориях без её страниц и подкатегорий. + +Не принимает параметров. + +Возвращает информацию о подкатегориях, принадлежащих категории. Тип – [FAQCategoryInfo]. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

протокол FAQCategoryInfo

+Представление информации о категории блока часто задаваемых вопросов. Предоставляет методы получения идентификатора и заголовка категории. + +Объекты FAQCategoryInfo могут быть получены с помощью метода getSubcategories(). + +

метод getId()

+Не принимает параметров. + +Возвращает идентификатор категории. Тип – Int. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getTitle()

+Не принимает параметров. + +Возвращает заголовок категории. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

протокол FAQItem

+Представление страницы блока часто задаваемых вопросов. Предоставляет методы получения информации о странице. + +Объекты FAQItem могут быть получены с completion функции getItem(id:completion:). + +

метод getId()

+Не принимает параметров. + +Возвращает идентификатор страницы. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getTitle()

+Не принимает параметров. + +Возвращает заголовок страницы. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getCategories()

+Страница может принадлежать нескольким категориям. + +Не принимает параметров. + +Возвращает идентификаторы категорий, которым принадлежи страница. Тип – [Int]. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getTags()

+У страницы есть набор тэгов. + +Не принимает параметров. + +Возвращает список тэгов, которыми отмечена страница. Тип – [String]. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getContent()

+Не принимает параметров. + +Возвращает содержание страницы. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getLikeCount()

+Страница содержит количество отметок "Нравится". + +Не принимает параметров. + +Возвращает количество отметок "Нравится" страницы. Тип – Int. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getDislikeCount()

+Страница содержит количество отметок "Не нравится". + +Не принимает параметров. + +Возвращает количество отметок "Не нравится" страницы. Тип – Int. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getUserRate()

+Страница может быть отмечена пользователем. + +Возварщает оценку пользователя. Тип – UserRate. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

перечисляемый тип UserRate

+Страница может быть отмечена пользователем. + +

случай LIKE

+Пользователю понравилась страница. + +

случай DISLIKE

+Пользователю не понравилась страница. + +

случай NO_RATE

+Страница не оценена пользователем. + +

протокол FAQSearchItem

+Краткая информация о страницах, полученных в результате поиска по ключевому слову. + +

метод getID()

+Не принимает параметров. + +Возвращает идентификатор страницы. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getTitle()

+Не принимает параметров. + +Возвращает заголовок страницы. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getScore()

+У страницы есть оценка поиска. Чем больше оценка, тем больше страница подходит под поисковой запрос. + +Не принимает параметров. + +Возвращает оценку поиска. Тип – Double. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

протокол FAQStructure

+Представление дерева (структуры) блока часто задаваемых вопросов. Предоставляет методы получения информации о дереве блока часто задаваемых вопросов. + +Объекты FAQStructure могут быть получены с помощью completion метода getStructure(id:completion). + +

метод getId()

+У дерева есть корневой элемент. + +Не принимает параметров. + +Возвращает идентификатор корневого элемента дерева. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getTitle()

+Не принимает параметров. + +Возвращает заголовок корня. Тип – String. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getType()

+Корень дерева может быть категорией или страницей. + +Не принимает параметров. + +Возвращает тип корневого элемента. Тип – RootType. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + +

метод getChildren()

+Не принимает параметров. + +Возвращает поддеревья, корнями которых являются прямые потомки корня дерева (структуры). Тип – [FAQStructure]. + +Не выбрасывает исключений. + +Не требует предварительного вызова никаких других методов. + + +

перечисляемый тип RootType

+Возможные типы корня дерева блока часто задаваемых вопросов (см. метод getType() протокола FAQStructure). + +

случай ITEM

+Корень дерева является страницей. + +

случай CATEGORY

+Корень дерева является категорией. + +

случай UNKNOWN

+Корень дерева имеет неизвестный тип для данной версии SDK. diff --git a/Example/Tests/SQLiteHistoryStorageTests.swift b/Example/Tests/SQLiteHistoryStorageTests.swift index 5ad02523..f5d1a0e8 100644 --- a/Example/Tests/SQLiteHistoryStorageTests.swift +++ b/Example/Tests/SQLiteHistoryStorageTests.swift @@ -101,6 +101,7 @@ class SQLiteHistoryStorageTests: XCTestCase { keyboard: nil, keyboardRequest: nil, operatorID: "1", + quote: nil, senderAvatarURLString: nil, senderName: "Name", type: MessageType.OPERATOR, @@ -112,7 +113,8 @@ class SQLiteHistoryStorageTests: XCTestCase { internalID: String(index), rawText: nil, read: true, - messageCanBeEdited: false)) + messageCanBeEdited: false, + messageCanBeReplied: false)) } return messages diff --git a/Example/WebimClientLibrary.xcodeproj/project.pbxproj b/Example/WebimClientLibrary.xcodeproj/project.pbxproj index 447d4322..1a8643ab 100644 --- a/Example/WebimClientLibrary.xcodeproj/project.pbxproj +++ b/Example/WebimClientLibrary.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 11EFE7D3231AC33500B64EFA /* FAQ methods documentation.md in Resources */ = {isa = PBXBuildFile; fileRef = 11EFE7D2231AC33500B64EFA /* FAQ methods documentation.md */; }; 3BD523E036E5E0145C40A893 /* Pods_WebimClientLibrary_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75BFCD5FB1955EE77D987E3C /* Pods_WebimClientLibrary_Tests.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACD81AFB9204008FA782 /* StartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* StartViewController.swift */; }; @@ -97,6 +98,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 11EFE7D2231AC33500B64EFA /* FAQ methods documentation.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "FAQ methods documentation.md"; sourceTree = ""; }; 188E01CE8A86B2460C60C156 /* Pods-WebimClientLibrary_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimClientLibrary_Tests.release.xcconfig"; path = "Target Support Files/Pods-WebimClientLibrary_Tests/Pods-WebimClientLibrary_Tests.release.xcconfig"; sourceTree = ""; }; 607FACD01AFB9204008FA782 /* WebimClientLibrary_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebimClientLibrary_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -409,6 +411,7 @@ children = ( CE24A015203ADC2D009EE7E7 /* Images */, CE54B2511FC452B9009C05BD /* Index.md */, + 11EFE7D2231AC33500B64EFA /* FAQ methods documentation.md */, ); name = Documentation; path = ../Documentation; @@ -567,6 +570,7 @@ 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, CE5A03BA1FDEB756009D320A /* Localizable.strings in Resources */, CE324E631FDECBD2004BB116 /* RatingViewController.xib in Resources */, + 11EFE7D3231AC33500B64EFA /* FAQ methods documentation.md in Resources */, 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, ); diff --git a/Info.plist b/Info.plist index fb82c0ca..9dfb3540 100644 --- a/Info.plist +++ b/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.27.0 + 3.27.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/README.md b/README.md index 101978ec..9b704bf8 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This library provides [_Webim SDK_ for _iOS_](https://webim.ru/integration/mobil Add following line for your target in your **Podfile**: ``` -pod 'WebimClientLibrary', :git => 'https://github.com/webim/webim-client-sdk-ios.git', :branch => 'master', :tag => '3.27.0' +pod 'WebimClientLibrary', :git => 'https://github.com/webim/webim-client-sdk-ios.git', :branch => 'master', :tag => '3.27.1' ``` `use_frameworks!` must be specified. @@ -24,7 +24,7 @@ pod 'WebimClientLibrary', :git => 'https://github.com/webim/webim-client-sdk-ios Add following line to your **Cartfile**: ``` -github "webim/webim-client-sdk-ios" ~> 3.27.0 +github "webim/webim-client-sdk-ios" ~> 3.27.1 ``` ### Additional notes @@ -38,7 +38,8 @@ Trying to integrate _WebimClientLibrary_ into your _Objective-C_ code? Try out o Previous _Objective-C_ version (version numbers 2.x.x) can be reached from **version2** branch. ## Release notes -* New FAQ functions. +* DB index bug fixes. +* UserDefaults bug fixes. ## Example diff --git a/WebimClientLibrary.podspec b/WebimClientLibrary.podspec index bf6888e3..fd5a82cd 100644 --- a/WebimClientLibrary.podspec +++ b/WebimClientLibrary.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'WebimClientLibrary' - s.version = '3.27.0' + s.version = '3.27.1' s.author = { 'Webim.ru Ltd.' => 'n.lazarev-zubov@webim.ru' } s.homepage = 'https://webim.ru/integration/mobile-sdk/ios-sdk-howto/' diff --git a/WebimClientLibrary/Backend/AbstractRequestLoop.swift b/WebimClientLibrary/Backend/AbstractRequestLoop.swift index 5c168a9d..dab46e95 100644 --- a/WebimClientLibrary/Backend/AbstractRequestLoop.swift +++ b/WebimClientLibrary/Backend/AbstractRequestLoop.swift @@ -93,7 +93,7 @@ class AbstractRequestLoop { func perform(request: URLRequest) throws -> Data { var requestWithUesrAngent = request - requestWithUesrAngent.setValue("iOS: Webim-Client 3.27.0; (\(UIDevice.current.model); \(UIDevice.current.systemVersion)); Bundle ID and version: \(Bundle.main.bundleIdentifier ?? "none") \(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "none")", forHTTPHeaderField: "User-Agent") + requestWithUesrAngent.setValue("iOS: Webim-Client 3.27.1; (\(UIDevice.current.model); \(UIDevice.current.systemVersion)); Bundle ID and version: \(Bundle.main.bundleIdentifier ?? "none") \(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "none")", forHTTPHeaderField: "User-Agent") var errorCounter = 0 var lastHTTPCode = -1 diff --git a/WebimClientLibrary/Backend/DeltaCallback.swift b/WebimClientLibrary/Backend/DeltaCallback.swift index c554dcc4..ee6b674a 100644 --- a/WebimClientLibrary/Backend/DeltaCallback.swift +++ b/WebimClientLibrary/Backend/DeltaCallback.swift @@ -40,14 +40,16 @@ final class DeltaCallback { // MARK: - Properties private let currentChatMessageMapper: MessageMapper private var currentChat: ChatItem? - private let readBeforeTimestampString = "read_before_timestamp" + private let userDefaultsKey: String private weak var messageHolder: MessageHolder? private weak var messageStream: MessageStreamImpl? private weak var historyPoller: HistoryPoller? // MARK: - Initialization - init(currentChatMessageMapper: MessageMapper) { + init(currentChatMessageMapper: MessageMapper, + userDefaultsKey: String) { self.currentChatMessageMapper = currentChatMessageMapper + self.userDefaultsKey = userDefaultsKey } // MARK: - Methods @@ -156,15 +158,14 @@ final class DeltaCallback { if let message = currentChatMessageMapper.map(message: messageItem) { if (message.getType() == MessageType.FILE_FROM_VISITOR || message.getType() != MessageType.VISITOR) && message.isReadByOperator() { let time = message.getTimeInMicrosecond() - if time > Int64(UserDefaults.standard.integer(forKey: readBeforeTimestampString)) { - UserDefaults.standard.set(time, forKey: readBeforeTimestampString) - historyPoller?.updateReadBeforeTimestamp(timestamp: time) + if time > historyPoller?.getReadBeforeTimestamp(byUserDefaultsKey: userDefaultsKey) ?? -1 { + historyPoller?.updateReadBeforeTimestamp(timestamp: time, byUserDefaultsKey: userDefaultsKey) } } } } } else { - UserDefaults.standard.set(-1, forKey: readBeforeTimestampString) + historyPoller?.updateReadBeforeTimestamp(timestamp: -1, byUserDefaultsKey: userDefaultsKey) } } @@ -254,9 +255,8 @@ final class DeltaCallback { currentChat?.set(messages: currentChatMessages) messageHolder?.changed(message: message) let time = message.getTimeInMicrosecond() - if time > UserDefaults.standard.integer(forKey: "readBeforeTimestampString") { - UserDefaults.standard.set(time, forKey: "readBeforeTimestampString") - historyPoller?.updateReadBeforeTimestamp(timestamp: time) + if time > historyPoller?.getReadBeforeTimestamp(byUserDefaultsKey: userDefaultsKey) ?? -1 { + historyPoller?.updateReadBeforeTimestamp(timestamp: time, byUserDefaultsKey: userDefaultsKey) } break } diff --git a/WebimClientLibrary/Backend/SQLiteHistoryStorage.swift b/WebimClientLibrary/Backend/SQLiteHistoryStorage.swift index 44f5a891..79b22b23 100644 --- a/WebimClientLibrary/Backend/SQLiteHistoryStorage.swift +++ b/WebimClientLibrary/Backend/SQLiteHistoryStorage.swift @@ -521,7 +521,8 @@ final class SQLiteHistoryStorage: HistoryStorage { _ = try db?.run(SQLiteHistoryStorage .history .createIndex(SQLiteHistoryStorage.timestamp, - unique: true)) + unique: true, + ifNotExists: true)) } catch { WebimInternalLogger.shared.log(entry: error.localizedDescription, verbosityLevel: .VERBOSE) diff --git a/WebimClientLibrary/Backend/Utilities/DepartmentFactory.swift b/WebimClientLibrary/Backend/Utilities/DepartmentFactory.swift index d1ef3091..c9d58bc1 100644 --- a/WebimClientLibrary/Backend/Utilities/DepartmentFactory.swift +++ b/WebimClientLibrary/Backend/Utilities/DepartmentFactory.swift @@ -46,7 +46,7 @@ final class DepartmentFactory { // MARK: - Methods func convert(departmentItem: DepartmentItem) -> DepartmentImpl { - var fullLogoURL: URL? = nil + var fullLogoURL: URL? if let logoURLString = departmentItem.getLogoURLString() { fullLogoURL = URL(string: serverURLString + logoURLString) } diff --git a/WebimClientLibrary/Backend/Utilities/InternalUtils.swift b/WebimClientLibrary/Backend/Utilities/InternalUtils.swift index ab111b1d..e572c50e 100644 --- a/WebimClientLibrary/Backend/Utilities/InternalUtils.swift +++ b/WebimClientLibrary/Backend/Utilities/InternalUtils.swift @@ -40,7 +40,7 @@ final class InternalUtils { static func createServerURLStringBy(accountName: String) -> String { var serverURLstring = accountName - if serverURLstring.range(of: "://") != nil { + if let _ = serverURLstring.range(of: "://") { if serverURLstring.last! == "/" { serverURLstring.removeLast() } diff --git a/WebimClientLibrary/Backend/Utilities/OperatorFactory.swift b/WebimClientLibrary/Backend/Utilities/OperatorFactory.swift index ac1db664..9af4c880 100644 --- a/WebimClientLibrary/Backend/Utilities/OperatorFactory.swift +++ b/WebimClientLibrary/Backend/Utilities/OperatorFactory.swift @@ -45,9 +45,10 @@ final class OperatorFactory { // MARK: - Methods func createOperatorFrom(operatorItem: OperatorItem?) -> OperatorImpl? { - return ((operatorItem == nil) ? nil : OperatorImpl(id: operatorItem!.getID(), - name: operatorItem!.getFullName(), - avatarURLString: ((operatorItem!.getAvatarURLString() == nil) ? nil : (serverURLString + operatorItem!.getAvatarURLString()!)))) + guard let operatorItem = operatorItem else { return nil } + return OperatorImpl(id: operatorItem.getID(), + name: operatorItem.getFullName(), + avatarURLString: ((operatorItem.getAvatarURLString() == nil) ? nil : (serverURLString + operatorItem.getAvatarURLString()!))) } } diff --git a/WebimClientLibrary/Implementation/WebimSessionImpl.swift b/WebimClientLibrary/Implementation/WebimSessionImpl.swift index 8c1aa767..10f93be9 100644 --- a/WebimClientLibrary/Implementation/WebimSessionImpl.swift +++ b/WebimClientLibrary/Implementation/WebimSessionImpl.swift @@ -135,7 +135,8 @@ final class WebimSessionImpl { let authorizationData = AuthorizationData(pageID: pageID, authorizationToken: authorizationToken) - let deltaCallback = DeltaCallback(currentChatMessageMapper: currentChatMessageMapper) + let deltaCallback = DeltaCallback(currentChatMessageMapper: currentChatMessageMapper, + userDefaultsKey: userDefaultsKey) let webimClient = WebimClientBuilder() .set(baseURL: serverURLString) @@ -165,7 +166,7 @@ final class WebimSessionImpl { var dbName = userDefaults?[UserDefaultsMainPrefix.historyDBname.rawValue] as? String if dbName == nil { - dbName = "webim_" + ClientSideID.generateClientSideID() + ".db" + dbName = "webim_\(ClientSideID.generateClientSideID()).db" if let userDefaults = userDefaults { var renewedUserDefaults = userDefaults renewedUserDefaults[UserDefaultsMainPrefix.historyDBname.rawValue] = dbName @@ -184,7 +185,7 @@ final class WebimSessionImpl { webimClient: webimClient, reachedHistoryEnd: historyMetaInformationStoragePreferences.isHistoryEnded(), queue: queue, - readBeforeTimestamp: Int64(UserDefaults.standard.integer(forKey: UserDefaultsMainPrefix.readBeforeTimestamp.rawValue))) + readBeforeTimestamp: (userDefaults?[UserDefaultsMainPrefix.readBeforeTimestamp.rawValue] ?? Int64(-1)) as! Int64) historyStorage = sqlhistoryStorage let historyMajorVersion = historyStorage.getMajorVersion() @@ -200,7 +201,7 @@ final class WebimSessionImpl { } } } else { - historyStorage = MemoryHistoryStorage(readBeforeTimestamp: Int64(UserDefaults.standard.integer(forKey: UserDefaultsMainPrefix.readBeforeTimestamp.rawValue))) + historyStorage = MemoryHistoryStorage(readBeforeTimestamp: (userDefaults?[UserDefaultsMainPrefix.readBeforeTimestamp.rawValue] ?? Int64(-1)) as! Int64) historyMetaInformationStoragePreferences = MemoryHistoryMetaInformationStorage() } @@ -484,8 +485,20 @@ final class HistoryPoller { self.hasHistoryRevision = hasHistoryRevision } - func updateReadBeforeTimestamp(timestamp: Int64) { + func updateReadBeforeTimestamp(timestamp: Int64, byUserDefaultsKey userDefaultsKey: String) { self.messageHolder.updateReadBeforeTimestamp(timestamp: timestamp) + if var userDefaults = UserDefaults.standard.dictionary(forKey: userDefaultsKey) { + userDefaults[UserDefaultsMainPrefix.readBeforeTimestamp.rawValue] = timestamp + UserDefaults.standard.set(userDefaults, forKey: userDefaultsKey) + } + } + + func getReadBeforeTimestamp(byUserDefaultsKey userDefaultsKey: String) -> Int64 { + let userDefaults = UserDefaults.standard.dictionary(forKey: userDefaultsKey) + if let readBeforeTimestamp = userDefaults?[UserDefaultsMainPrefix.readBeforeTimestamp.rawValue] { + return readBeforeTimestamp as! Int64 + } + return Int64(-1) } // MARK: Private methods