Commit 9d7e3d45 authored by IOS Developer's avatar IOS Developer
Browse files

add logic for chat messages base impl

parent 565e8e62
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildSystemType</key>
<string>Original</string>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildLocationStyle</key>
<string>UseAppPreferences</string>
<key>CustomBuildLocationType</key>
<string>RelativeToDerivedData</string>
<key>DerivedDataLocationStyle</key>
<string>Default</string>
<key>IssueFilterStyle</key>
<string>ShowActiveSchemeOnly</string>
<key>LiveSourceIssuesEnabled</key>
<true/>
<key>ShowSharedSchemesAutomaticallyEnabled</key>
<true/>
</dict>
</plist>
......@@ -9,6 +9,8 @@
import UIKit
import CoreData
import Firebase
import GoogleMaps
import GooglePlaces
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
......@@ -17,6 +19,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Override point for customization after application launch.
FirebaseApp.configure()
GMSServices.provideAPIKey(APIKeys.googleMap)
GMSPlacesClient.provideAPIKey(APIKeys.googleMap)
return true
}
......
//
// ChatRoom.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
class ChatRoom: NSObject {
var inputStream: InputStream!
var outputStream: OutputStream!
var username: String = ""
let maxReadLength = 4096
weak var delegate: ChatRoomDelegate?
func setupNetworkCommunication() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
"127.0.0.1" as CFString,
80,
&readStream,
&writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)
inputStream.open()
outputStream.open()
inputStream.delegate = self
}
func joinChat(username: String) {
let data = "iam:\(username)".data(using: .utf8)!
self.username = username
_ = data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}
func send(message: String) {
let data = "msg:\(message)".data(using: .utf8)!
_ = data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}
func stopChatSession() {
inputStream.close()
outputStream.close()
}
}
extension ChatRoom: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case .hasBytesAvailable:
print("new message received")
readAvailableBytes(stream: aStream as! InputStream)
case .endEncountered:
print("The end of the stream has been reached.")
stopChatSession()
case .errorOccurred:
print("error occurred")
case .hasSpaceAvailable:
print("has space available")
default:
print("some other event...")
}
}
private func readAvailableBytes(stream: InputStream) {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength)
while stream.hasBytesAvailable {
let numberOfBytesRead = inputStream.read(buffer, maxLength: maxReadLength)
if numberOfBytesRead < 0, let error = stream.streamError {
print(error)
break
}
if let message = processedMessageString(buffer: buffer, length: numberOfBytesRead) {
delegate?.received(message: message)
}
}
}
private func processedMessageString(buffer: UnsafeMutablePointer<UInt8>, length: Int) -> Message? {
guard let stringArray = String(
bytesNoCopy: buffer,
length: length,
encoding: .utf8,
freeWhenDone: true)?.components(separatedBy: ":"),
let name = stringArray.first,
let message = stringArray.last else { return nil }
let messageSender: MessageSender = (name == self.username) ? .ourself : .someoneElse
return Message(message: message, messageSender: messageSender, username: name, messageTime: Date(), user: CommonUserModel.init())
}
}
//
// ChatRoomDelegate.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
protocol ChatRoomDelegate: class {
func received(message: Message)
}
//
// ChatController.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
class ChatController {
var chatRoom: ChatRoom!
var messages: [Message] = []
var delegate: ChatControllerDelegte?
var serverApi: ChatServerApiInterface = ChatApiRequests()
var user: UserModelInterface!
var page: Int = 0
init(delegate: ChatControllerDelegte, with user: UserModelInterface) {
self.user = user
chatRoom = ChatRoom()
chatRoom.delegate = self
chatRoom.setupNetworkCommunication()
guard let userName = user.userDisplayName else { return }
chatRoom.joinChat(username: userName)
}
func sendMessage(with text: String) {
chatRoom.send(message: text)
}
func stopChat() {
chatRoom.stopChatSession()
}
func loadMessages() {
serverApi.loadMessages(withUser: self.user, page: page, completion: { (messages, error) in
if let error = error {
print(ErrorDescriptor.description(error: error))
} else if let messages = messages{
self.messages = messages
self.delegate?.messagesWasLoad()
self.page += 1
}
})
}
}
extension ChatController: ChatRoomDelegate {
func received(message: Message) {
messages.append(message)
delegate?.receiveMessage(message: message)
}
}
//
// ChatListController.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
class ChatListController {
var chatModels: [ChatModel] = []
var serverApi: ChatServerApiInterface = ChatApiRequests()
var delegate: ChatListControllerDelegate?
init(with delegate: ChatListControllerDelegate) {
self.delegate = delegate
}
func loadChats() {
serverApi.getChatList { (chatModels, error) in
if let error = error {
print(ErrorDescriptor.description(error: error))
} else if let chatModels = chatModels {
self.chatModels = chatModels
self.delegate?.chatsDidLoad()
}
}
}
func getChatModelByIndex(index: Int) -> ChatModel? {
return index <= chatModels.endIndex ? chatModels[index] : nil
}
}
//
// ChatControllerDelegate.swift
// AuthModule
//
// Created by Alexander on 12.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
protocol ChatControllerDelegte {
func receiveMessage(message: Message)
func messagesWasLoad()
}
//
// ChatListControllerDelegate.swift
// AuthModule
//
// Created by Alexander on 05.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
protocol ChatListControllerDelegate {
func chatsDidLoad()
}
//
// ChatModel.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
enum ChatModelStatus {
case base, unread
}
struct ChatModel {
var user: UserModelInterface
var message: Message
var status: ChatModelStatus
}
//
// Message.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
enum MessageSender {
case ourself
case someoneElse
}
struct Message {
let message: String
let senderUsername: String
let messageSender: MessageSender
let messageTime: Date
let user: UserModelInterface
init(message: String, messageSender: MessageSender, username: String, messageTime: Date, user: UserModelInterface) {
self.message = message
self.messageSender = messageSender
self.senderUsername = username
self.messageTime = messageTime
self.user = user
}
}
//
// ChatNavigationModule.swift
// AuthModule
//
// Created by Alexander on 05.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
class ChatNavigationModule: NavigationModuleAbstract {
override func startFlow() -> UIViewController {
let chatListViewController = ChatListViewController(chatNavigationModule: self)
navigationController.setViewControllers([chatListViewController], animated: true)
currentViewController = chatListViewController
return navigationController
}
func pushChatViewController(with user: UserModelInterface) {
let chatViewController = ChatViewController.init(chatNavigationModule: self, with: user)
navigationController.pushViewController(chatViewController, animated: true)
currentViewController = chatViewController
}
func popViewController() {
navigationController.popViewController(animated: true)
currentViewController = navigationController.topViewController
}
override func endFlow() {
// navigationRouterModuleDelegate.startNextNavigationModule(nextNavigationModule: AuthNavigationModule.self)
}
}
//
// ChatApiRequests.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
class ChatApiRequests: ChatServerApiInterface {
func getChatList(completion: @escaping ([ChatModel]?, Error?) -> ()) {
}
func uploadMediafile(with data: Data, completion: @escaping (String?, Error?) -> ()) {
}
func loadMessages(withUser: UserModelInterface, page: Int, completion: @escaping ([Message]?, Error?) -> ()) {
}
}
//
// ChatServerApiInterface.swift
// AuthModule
//
// Created by Alexander on 04.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import Foundation
protocol ChatServerApiInterface {
func getChatList(completion: @escaping ([ChatModel]?, Error?) -> ())
func uploadMediafile(with data: Data, completion: @escaping (String?, Error?) -> ())
func loadMessages(withUser: UserModelInterface, page: Int, completion: @escaping ([Message]?, Error?) -> ())
}
//
// ChatListTableViewCell.swift
// AuthModule
//
// Created by Alexander on 05.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import UIKit
import Kingfisher
class ChatListTableViewCell: UITableViewCell {
@IBOutlet weak var userNameLabel: UILabel!
@IBOutlet weak var userAvatar: UIImageView!
@IBOutlet weak var shortMessageLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
selectionStyle = .none
}
func fillWith(model: ChatModel?) {
guard let model = model else { return }
userNameLabel.text = model.user.userDisplayName
shortMessageLabel.text = model.message.message
if let stringUrl = model.user.userImageUrl,
let imageUrl = URL.init(string: stringUrl) {
userAvatar.kf.setImage(with: imageUrl)
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatListTableViewCell" rowHeight="136" id="KGk-i7-Jjw" customClass="ChatListTableViewCell" customModule="AuthModule" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="501" height="136"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="501" height="136"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="sz1-OG-sqG">
<rect key="frame" x="20" y="43" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="W2c-Ph-qQQ"/>
<constraint firstAttribute="width" constant="50" id="kYC-0u-gJt"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="User name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hOm-eK-ENc">
<rect key="frame" x="86" y="43" width="78.5" height="19.5"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Short mesage" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bMS-qA-4YG">
<rect key="frame" x="86" y="70.5" width="102" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="hOm-eK-ENc" firstAttribute="top" secondItem="sz1-OG-sqG" secondAttribute="top" id="07D-d5-cIv"/>
<constraint firstItem="sz1-OG-sqG" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="8CG-mc-3fs"/>
<constraint firstItem="bMS-qA-4YG" firstAttribute="leading" secondItem="sz1-OG-sqG" secondAttribute="trailing" constant="16" id="Cmw-QN-2lc"/>
<constraint firstItem="bMS-qA-4YG" firstAttribute="top" secondItem="hOm-eK-ENc" secondAttribute="bottom" constant="8" id="ne6-zG-Gem"/>
<constraint firstItem="hOm-eK-ENc" firstAttribute="leading" secondItem="sz1-OG-sqG" secondAttribute="trailing" constant="16" id="u6B-wl-yme"/>
<constraint firstItem="sz1-OG-sqG" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="vWh-GP-Xya"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="shortMessageLabel" destination="bMS-qA-4YG" id="x9E-qn-be5"/>
<outlet property="userAvatar" destination="sz1-OG-sqG" id="dnE-Nn-O6j"/>
<outlet property="userNameLabel" destination="hOm-eK-ENc" id="LuO-KN-CB1"/>
</connections>
<point key="canvasLocation" x="-70" y="92"/>
</tableViewCell>
</objects>
</document>
//
// ChatListViewController.swift
// AuthModule
//
// Created by Alexander on 05.06.2020.
// Copyright © 2020 triare. All rights reserved.
//
import UIKit
class ChatListViewController: UIViewContr