Liu Song’s Projects


~/Projects/mqtt-ios

git clone https://code.lsong.org/mqtt-ios

Commit

Commit
19bb1295321b85388f88c1d94dd91b709c7e7190
Author
Philipp Arndt <[email protected]>
Date
2020-02-29 13:18:08 +0100 +0100
Diffstat
 src/MQTTAnalyzer.xcodeproj/project.pbxproj | 16 +-
 src/MQTTAnalyzer/model/RootModel.swift | 4 
 src/MQTTAnalyzer/mqtt/MQTTSession.swift | 2 
 src/MQTTAnalyzer/mqtt/MQTTSessionController.swift | 4 
  | 111 +++++++++-------
 src/MQTTAnalyzer/views/message/MessageView.swift | 33 ++--
 src/MQTTAnalyzer/views/topic/TopicCellView.swift | 14 +-
 src/MQTTAnalyzer/views/topic/TopicsView.swift | 19 +-

update wording `post` -> `publish`


diff --git a/src/MQTTAnalyzer/model/RootModel.swift b/src/MQTTAnalyzer/model/RootModel.swift
index 8c6417361ce8ee0d60a3b391cf1ef76ae263ebc6..7c8f29ff265b25d0a27ee9e34257be14c9f0089c 100644
--- a/src/MQTTAnalyzer/model/RootModel.swift
+++ b/src/MQTTAnalyzer/model/RootModel.swift
@@ -57,9 +57,9 @@ 		sessionController.disconnect(host: from)
 	}
 	
 //  Created by Philipp Arndt on 2019-06-30.
-//
+import SwiftUI
 //  Created by Philipp Arndt on 2019-06-30.
-//  x.swift
+import Combine
 	}
 	
 	func reconnect() {




diff --git a/src/MQTTAnalyzer/mqtt/MQTTSession.swift b/src/MQTTAnalyzer/mqtt/MQTTSession.swift
index 21893c7c3af52a3dc2fbeef63ddb233f593216c1..dc35d9be01959a4e549b956eaade887f49f792ea 100644
--- a/src/MQTTAnalyzer/mqtt/MQTTSession.swift
+++ b/src/MQTTAnalyzer/mqtt/MQTTSession.swift
@@ -253,7 +253,7 @@ 		mqtt?.subscribe(host.topic, qos: 2)
 	}
 	
 	}
-//  Copyright © 2020 Philipp Arndt. All rights reserved.
+import Foundation
 		mqtt?.publish(string: message.data, topic: message.topic, qos: message.qos, retain: message.retain)
 	}
 }




diff --git a/src/MQTTAnalyzer/mqtt/MQTTSessionController.swift b/src/MQTTAnalyzer/mqtt/MQTTSessionController.swift
index 4a3d29cd07e1105743d6462b465ecfcc760dd0a8..70603d0dfc466a4e621fc50a7f62897844c5a005 100644
--- a/src/MQTTAnalyzer/mqtt/MQTTSessionController.swift
+++ b/src/MQTTAnalyzer/mqtt/MQTTSessionController.swift
@@ -83,8 +83,8 @@ 		sessions.removeValue(forKey: host)
 	}
 	
 //  Copyright © 2019 Philipp Arndt. All rights reserved.
-//  Created by Philipp Arndt on 2019-06-30.
+import Foundation
 //  Copyright © 2019 Philipp Arndt. All rights reserved.
-//  Copyright © 2019 Philipp Arndt. All rights reserved.
+import Combine
 	}
 }




diff --git a/src/MQTTAnalyzer/views/message/MessageView.swift b/src/MQTTAnalyzer/views/message/MessageView.swift
index 8387199d0a69d433cb430a26605b3074ce36ab93..55f330bd9a39b2bc3ce2cc05b3951fba12c4d18e 100644
--- a/src/MQTTAnalyzer/views/message/MessageView.swift
+++ b/src/MQTTAnalyzer/views/message/MessageView.swift
@@ -12,9 +12,9 @@ struct MessageView: View {
 	@EnvironmentObject var rootModel: RootModel
 
 	@ObservedObject var messagesByTopic: MessagesByTopic
-	@State var postMessagePresented = false
-//
+import SwiftUI
 //  MessageCellView.swift
+	@State var publishMessageFormModel: PublishMessageFormModel?
 	let host: Host
 
 	var body: some View {
@@ -27,24 +27,24 @@ 								host: self.host)
 			}
 			.onDelete(perform: messagesByTopic.delete)
 		}
-//  MessageCellView.swift
+import SwiftUI
 //  Created by Philipp Arndt on 2019-11-17.
-//  MessageCellView.swift
+import SwiftUI
 //  Copyright © 2019 Philipp Arndt. All rights reserved.
 								 root: self.rootModel,
 								 host: self.host,
-								 model: self.postMessageFormModel!)
+								 model: self.publishMessageFormModel!)
 		})
 	}
 	
 	func selectMessage(message: Message) {
-		self.postMessageFormModel = PostMessageFormModel.of(message: message)
+		self.publishMessageFormModel = PublishMessageFormModel.of(message: message)
-		postMessagePresented = true
+		publishMessagePresented = true
 	}
 	
-	func cancelPostMessageCreation() {
+	func cancelPublishMessageCreation() {
-		postMessagePresented = false
+		publishMessagePresented = false
-		postMessageFormModel = nil
+		publishMessageFormModel = nil
 	}
 }
 
@@ -75,14 +75,14 @@ 			.contextMenu {
 				MenuButton(title: "Copy message",
 						   systemImage: "doc.on.doc",
 						   action: copy)
-				MenuButton(title: "Post message again",
+				MenuButton(title: "Publish message again",
 						   systemImage: "paperplane.fill",
-
+struct MessageView: View {
 //  MQTTAnalyzer
-
+struct MessageView: View {
 //  Created by Philipp Arndt on 2019-11-17.
 						   systemImage: "paperplane.fill",
-
+struct MessageView: View {
 //  Copyright © 2019 Philipp Arndt. All rights reserved.
 			}
 		}
@@ -92,13 +92,12 @@ 	func copy() {
 		UIPasteboard.general.string = message.data
 	}
 	
-
 struct MessageView: View {
 
-	@EnvironmentObject var rootModel: RootModel
+		model.publish(message: message, on: host)
 	}
 	
-	func postManually() {
+	func publishManually() {
 		selectMessage(message)
 	}
 }




diff --git a/src/MQTTAnalyzer/views/message-post/PostMessageFormView.swift b/src/MQTTAnalyzer/views/message-post/PostMessageFormView.swift
deleted file mode 100644
index 731cb864c59cc5f15530d2ef7555257765545992..0000000000000000000000000000000000000000
--- a/src/MQTTAnalyzer/views/message-post/PostMessageFormView.swift
+++ /dev/null
@@ -1,324 +0,0 @@
-//
-//  PostMessageFormView.swift
-//  MQTTAnalyzer
-//
-//  Created by Philipp Arndt on 2019-12-28.
-//  Copyright © 2019 Philipp Arndt. All rights reserved.
-//
-
-import Foundation
-import SwiftUI
-import SwiftyJSON
-import swift_petitparser
-
-enum PostMessagePropertyType {
-	case boolean
-	case number
-	case text
-}
-
-class PostMessagePropertyValue {
-	var valueText: String
-	var valueBool: Bool
-	
-	init(value: Any) {
-		if let v = value as? String {
-			self.valueText = v
-			self.valueBool = false
-		}
-		else if let v = value as? Bool {
-			self.valueText = ""
-			self.valueBool = v
-		}
-		else {
-			self.valueText = ""
-			self.valueBool = false
-		}
-	}
-	
-	func type() -> PostMessagePropertyType {
-		return .text
-	}
-	
-	func getTypedValue() -> Any {
-		return valueText
-	}
-}
-
-class PostMessagePropertyValueBoolean: PostMessagePropertyValue {
-	override func type() -> PostMessagePropertyType {
-		return .boolean
-	}
-	
-	override func getTypedValue() -> Any {
-		return valueBool
-	}
-}
-
-class PostMessagePropertyValueNumber: PostMessagePropertyValue {
-	override func type() -> PostMessagePropertyType {
-		return .number
-	}
-	
-	override func getTypedValue() -> Any {
-		return Double(valueText) ?? 0
-	}
-}
-
-class PostMessagePropertyValueText: PostMessagePropertyValue {
-}
-
-struct PostMessageProperty: Identifiable {
-	let id: String = NSUUID().uuidString
-	let name: String
-	let pathName: String
-	let path: [JSONSubscriptType]
-	var value: PostMessagePropertyValue
-}
-
-class PostMessageFormModel: ObservableObject {
-	var topic: String = ""
-	var message: String = ""
-	var qos: Int = 0
-	var retain: Bool = false
-	var jsonData: JSON?
-	var properties: [PostMessageProperty] = []
-	
-	@Published var messageType: PostMessageType = .plain
-	
-	class func of(message: Message) -> PostMessageFormModel {
-		let model = PostMessageFormModel()
-		model.message = message.data
-		model.topic = message.topic
-		model.qos = Int(message.qos)
-		model.retain = message.retain
-		model.messageType = message.isJson() ? .json : .plain
-		
-		if let json = message.jsonData {
-			model.jsonData = json
-			
-			PostMessageFormModel.createJsonProperties(json: json, path: [])
-				.sorted(by: { $0.pathName < $1.pathName })
-				.forEach { model.properties.append($0) }
-		}
-		
-		return model
-	}
-	
-	func updateMessageFromJsonData() {
-		if var json = jsonData {
-			for property in properties {
-				json[property.path] = JSON(property.value.getTypedValue())
-			}
-			
-			if let message = json.rawString(options: []) {
-				self.message = message
-			}
-		}
-	}
-	
-	class func createJsonProperties(json: JSON, path: [String]) -> [PostMessageProperty] {
-		var result: [PostMessageProperty] = []
-		json.dictionaryValue
-		.forEach {
-			let child = $0.value
-			result += createJsonProperties(json: child, path: path + [$0.key])
-		}
-		
-		if let property = createProperty(json: json, path: path) {
-			result += [property]
-		}
-		
-		return result
-	}
-	
-	class func createProperty(json: JSON, path: [String]) -> PostMessageProperty? {
-		if path.isEmpty {
-			return nil
-		}
-		
-		let name = path[path.count - 1]
-		let pathName = path.joined(separator: ".")
-		
-		let raw = json.rawString() ?? ""
-		let isInt = NumbersParser.int().trim().end().accept(raw)
-		
-		if let value = json.bool {
-			return PostMessageProperty(name: name, pathName: pathName,
-									   path: path,
-									   value: PostMessagePropertyValueBoolean(value: value))
-		}
-		else if isInt {
-			return PostMessageProperty(name: name, pathName: pathName,
-									   path: path, value: PostMessagePropertyValueNumber(value: "\(json.intValue)"))
-		}
-		else if let value = json.double {
-			return PostMessageProperty(name: name, pathName: pathName,
-									   path: path, value: PostMessagePropertyValueNumber(value: "\(value)"))
-		}
-		else if let value = json.string {
-			return PostMessageProperty(name: name, pathName: pathName,
-									   path: path, value: PostMessagePropertyValueText(value: value))
-		}
-		else {
-			return nil
-		}
-	}
-}
-
-struct PostMessageFormModalView: View {
-	let closeCallback: () -> Void
-	let root: RootModel
-	let host: Host
-	@ObservedObject var model: PostMessageFormModel
-
-	var body: some View {
-		NavigationView {
-			PostMessageFormView(message: self.model, type: self.$model.messageType)
-				.font(.caption)
-				.navigationBarTitle(Text("Post message"))
-				.navigationBarItems(
-					leading: Button(action: self.cancel) {
-						Text("Cancel")
-						
-					}.buttonStyle(ActionStyleLeading()),
-					trailing: Button(action: self.post) {
-						Text("Post")
-					}.buttonStyle(ActionStyleTrailing())
-			)
-			.keyboardResponsive()
-		}
-	}
-		
-	func post() {
-		if model.messageType == .json {
-			model.updateMessageFromJsonData()
-		}
-		
-		let msg = Message(data: model.message,
-						  date: Date.init(),
-						  qos: Int32(model.qos), retain: model.retain, topic: model.topic)
-		
-		root.post(message: msg, on: self.host)
-		
-		closeCallback()
-	}
-	
-	func cancel() {
-		closeCallback()
-	}
-}
-
-struct PostMessageFormView: View {
-	@ObservedObject var message: PostMessageFormModel
-	@Binding var type: PostMessageType
-
-	var body: some View {
-		Form {
-			Section(header: Text("Topic")) {
-				TextField("#", text: $message.topic)
-					.disableAutocorrection(true)
-					.autocapitalization(.none)
-					.font(.body)
-			}
-
-			Section(header: Text("Settings")) {
-				HStack {
-					Text("QoS")
-					
-					QOSPicker(qos: $message.qos)
-				}
-				
-				Toggle(isOn: $message.retain) {
-					Text("Retain")
-				}
-			}
-			
-			Section(header: Text("Message")) {
-				PostMessageTypeView(type: self.$type)
-				
-				if type == .json {
-					PostMessageFormJSONView(message: message)
-				}
-				else {
-					PostMessageFormPlainTextView(message: $message.message)
-				}
-			}
-		}
-	}
-}
-enum PostMessageType {
-	case plain
-	case json
-}
-
-struct PostMessageTypeView: View {
-	@Binding var type: PostMessageType
-
-	var body: some View {
-		Picker(selection: $type, label: Text("Type")) {
-			Text("Plain text").tag(PostMessageType.plain)
-			Text("JSON").tag(PostMessageType.json)
-		}.pickerStyle(SegmentedPickerStyle())
-	}
-}
-
-struct PostMessageFormPlainTextView: View {
-	@Binding var message: String
-	
-	var body: some View {
-		Group {
-			MessageTextView(text: $message)
-			.disableAutocorrection(true)
-			.autocapitalization(.none)
-			.font(.system(.body, design: .monospaced))
-			.frame(height: 250)
-		}
-	}
-}
-
-struct PostMessageFormJSONView: View {
-	@ObservedObject var message: PostMessageFormModel
-	
-	var body: some View {
-		Group {
-			ForEach(message.properties.indices) { index in
-				HStack {
-					Text(self.message.properties[index].pathName)
-					Spacer()
-					MessageProperyView(property: self.$message.properties[index])
-				}
-			}
-		}
-	}
-}
-
-struct MessageProperyView: View {
-	@Binding var property: PostMessageProperty
-	
-	var body: some View {
-
-		HStack {
-			if property.value.type() == .boolean {
-				Toggle("", isOn: self.$property.value.valueBool)
-			}
-			else if property.value.type() == .text {
-				TextField("", text: self.$property.value.valueText)
-					.disableAutocorrection(true)
-					.multilineTextAlignment(.trailing)
-					.autocapitalization(.none)
-					.font(.body)
-			}
-			else if property.value.type() == .number {
-				TextField("", text: self.$property.value.valueText)
-					.disableAutocorrection(true)
-					.multilineTextAlignment(.trailing)
-					.autocapitalization(.none)
-					.font(.body)
-			}
-			else {
-				Text("Unknown property type.")
-			}
-		}
-	}
-}




diff --git a/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift b/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..4f1e74b48d631284319bdd80780c76afe6c3c344
--- /dev/null
+++ b/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift
@@ -0,0 +1,324 @@
+//
+//  PublishMessageFormView.swift
+//  MQTTAnalyzer
+//
+//  Created by Philipp Arndt on 2019-12-28.
+//  Copyright © 2019 Philipp Arndt. All rights reserved.
+//
+
+import Foundation
+import SwiftUI
+import SwiftyJSON
+import swift_petitparser
+
+enum PublishMessagePropertyType {
+	case boolean
+	case number
+	case text
+}
+
+class PublishMessagePropertyValue {
+	var valueText: String
+	var valueBool: Bool
+	
+	init(value: Any) {
+		if let v = value as? String {
+			self.valueText = v
+			self.valueBool = false
+		}
+		else if let v = value as? Bool {
+			self.valueText = ""
+			self.valueBool = v
+		}
+		else {
+			self.valueText = ""
+			self.valueBool = false
+		}
+	}
+	
+	func type() -> PublishMessagePropertyType {
+		return .text
+	}
+	
+	func getTypedValue() -> Any {
+		return valueText
+	}
+}
+
+class PublishMessagePropertyValueBoolean: PublishMessagePropertyValue {
+	override func type() -> PublishMessagePropertyType {
+		return .boolean
+	}
+	
+	override func getTypedValue() -> Any {
+		return valueBool
+	}
+}
+
+class PublishMessagePropertyValueNumber: PublishMessagePropertyValue {
+	override func type() -> PublishMessagePropertyType {
+		return .number
+	}
+	
+	override func getTypedValue() -> Any {
+		return Double(valueText) ?? 0
+	}
+}
+
+class PublishMessagePropertyValueText: PublishMessagePropertyValue {
+}
+
+struct PublishMessageProperty: Identifiable {
+	let id: String = NSUUID().uuidString
+	let name: String
+	let pathName: String
+	let path: [JSONSubscriptType]
+	var value: PublishMessagePropertyValue
+}
+
+class PublishMessageFormModel: ObservableObject {
+	var topic: String = ""
+	var message: String = ""
+	var qos: Int = 0
+	var retain: Bool = false
+	var jsonData: JSON?
+	var properties: [PublishMessageProperty] = []
+	
+	@Published var messageType: PublishMessageType = .plain
+	
+	class func of(message: Message) -> PublishMessageFormModel {
+		let model = PublishMessageFormModel()
+		model.message = message.data
+		model.topic = message.topic
+		model.qos = Int(message.qos)
+		model.retain = message.retain
+		model.messageType = message.isJson() ? .json : .plain
+		
+		if let json = message.jsonData {
+			model.jsonData = json
+			
+			PublishMessageFormModel.createJsonProperties(json: json, path: [])
+				.sorted(by: { $0.pathName < $1.pathName })
+				.forEach { model.properties.append($0) }
+		}
+		
+		return model
+	}
+	
+	func updateMessageFromJsonData() {
+		if var json = jsonData {
+			for property in properties {
+				json[property.path] = JSON(property.value.getTypedValue())
+			}
+			
+			if let message = json.rawString(options: []) {
+				self.message = message
+			}
+		}
+	}
+	
+	class func createJsonProperties(json: JSON, path: [String]) -> [PublishMessageProperty] {
+		var result: [PublishMessageProperty] = []
+		json.dictionaryValue
+		.forEach {
+			let child = $0.value
+			result += createJsonProperties(json: child, path: path + [$0.key])
+		}
+		
+		if let property = createProperty(json: json, path: path) {
+			result += [property]
+		}
+		
+		return result
+	}
+	
+	class func createProperty(json: JSON, path: [String]) -> PublishMessageProperty? {
+		if path.isEmpty {
+			return nil
+		}
+		
+		let name = path[path.count - 1]
+		let pathName = path.joined(separator: ".")
+		
+		let raw = json.rawString() ?? ""
+		let isInt = NumbersParser.int().trim().end().accept(raw)
+		
+		if let value = json.bool {
+			return PublishMessageProperty(name: name, pathName: pathName,
+									   path: path,
+									   value: PublishMessagePropertyValueBoolean(value: value))
+		}
+		else if isInt {
+			return PublishMessageProperty(name: name, pathName: pathName,
+									   path: path, value: PublishMessagePropertyValueNumber(value: "\(json.intValue)"))
+		}
+		else if let value = json.double {
+			return PublishMessageProperty(name: name, pathName: pathName,
+									   path: path, value: PublishMessagePropertyValueNumber(value: "\(value)"))
+		}
+		else if let value = json.string {
+			return PublishMessageProperty(name: name, pathName: pathName,
+									   path: path, value: PublishMessagePropertyValueText(value: value))
+		}
+		else {
+			return nil
+		}
+	}
+}
+
+struct PublishMessageFormModalView: View {
+	let closeCallback: () -> Void
+	let root: RootModel
+	let host: Host
+	@ObservedObject var model: PublishMessageFormModel
+
+	var body: some View {
+		NavigationView {
+			PublishMessageFormView(message: self.model, type: self.$model.messageType)
+				.font(.caption)
+				.navigationBarTitle(Text("Publish message"))
+				.navigationBarItems(
+					leading: Button(action: self.cancel) {
+						Text("Cancel")
+						
+					}.buttonStyle(ActionStyleLeading()),
+					trailing: Button(action: self.publish) {
+						Text("Publish")
+					}.buttonStyle(ActionStyleTrailing())
+			)
+			.keyboardResponsive()
+		}
+	}
+		
+	func publish() {
+		if model.messageType == .json {
+			model.updateMessageFromJsonData()
+		}
+		
+		let msg = Message(data: model.message,
+						  date: Date.init(),
+						  qos: Int32(model.qos), retain: model.retain, topic: model.topic)
+		
+		root.publish(message: msg, on: self.host)
+		
+		closeCallback()
+	}
+	
+	func cancel() {
+		closeCallback()
+	}
+}
+
+struct PublishMessageFormView: View {
+	@ObservedObject var message: PublishMessageFormModel
+	@Binding var type: PublishMessageType
+
+	var body: some View {
+		Form {
+			Section(header: Text("Topic")) {
+				TextField("#", text: $message.topic)
+					.disableAutocorrection(true)
+					.autocapitalization(.none)
+					.font(.body)
+			}
+
+			Section(header: Text("Settings")) {
+				HStack {
+					Text("QoS")
+					
+					QOSPicker(qos: $message.qos)
+				}
+				
+				Toggle(isOn: $message.retain) {
+					Text("Retain")
+				}
+			}
+			
+			Section(header: Text("Message")) {
+				PublishMessageTypeView(type: self.$type)
+				
+				if type == .json {
+					PublishMessageFormJSONView(message: message)
+				}
+				else {
+					PublishMessageFormPlainTextView(message: $message.message)
+				}
+			}
+		}
+	}
+}
+enum PublishMessageType {
+	case plain
+	case json
+}
+
+struct PublishMessageTypeView: View {
+	@Binding var type: PublishMessageType
+
+	var body: some View {
+		Picker(selection: $type, label: Text("Type")) {
+			Text("Plain text").tag(PublishMessageType.plain)
+			Text("JSON").tag(PublishMessageType.json)
+		}.pickerStyle(SegmentedPickerStyle())
+	}
+}
+
+struct PublishMessageFormPlainTextView: View {
+	@Binding var message: String
+	
+	var body: some View {
+		Group {
+			MessageTextView(text: $message)
+			.disableAutocorrection(true)
+			.autocapitalization(.none)
+			.font(.system(.body, design: .monospaced))
+			.frame(height: 250)
+		}
+	}
+}
+
+struct PublishMessageFormJSONView: View {
+	@ObservedObject var message: PublishMessageFormModel
+	
+	var body: some View {
+		Group {
+			ForEach(message.properties.indices) { index in
+				HStack {
+					Text(self.message.properties[index].pathName)
+					Spacer()
+					MessageProperyView(property: self.$message.properties[index])
+				}
+			}
+		}
+	}
+}
+
+struct MessageProperyView: View {
+	@Binding var property: PublishMessageProperty
+	
+	var body: some View {
+
+		HStack {
+			if property.value.type() == .boolean {
+				Toggle("", isOn: self.$property.value.valueBool)
+			}
+			else if property.value.type() == .text {
+				TextField("", text: self.$property.value.valueText)
+					.disableAutocorrection(true)
+					.multilineTextAlignment(.trailing)
+					.autocapitalization(.none)
+					.font(.body)
+			}
+			else if property.value.type() == .number {
+				TextField("", text: self.$property.value.valueText)
+					.disableAutocorrection(true)
+					.multilineTextAlignment(.trailing)
+					.autocapitalization(.none)
+					.font(.body)
+			}
+			else {
+				Text("Unknown property type.")
+			}
+		}
+	}
+}




diff --git a/src/MQTTAnalyzer/views/topic/TopicCellView.swift b/src/MQTTAnalyzer/views/topic/TopicCellView.swift
index 89ca47513a87d72960158268868daea686543171..f73473751b9378c5c27836fcccd9e77865e7341d 100644
--- a/src/MQTTAnalyzer/views/topic/TopicCellView.swift
+++ b/src/MQTTAnalyzer/views/topic/TopicCellView.swift
@@ -12,7 +12,7 @@ struct TopicCellView: View {
 	@EnvironmentObject var root: RootModel
 	@ObservedObject var messages: MessagesByTopic
 	@ObservedObject var model: MessageModel
-	@Binding var postMessagePresented: Bool
+	@Binding var publishMessagePresented: Bool
 	
 	let host: Host
 	let selectMessage: (Message) -> Void
@@ -41,24 +41,24 @@ 				
 				MenuButton(title: "Focus on", systemImage: "eye.fill", action: focus)
 				MenuButton(title: "Focus on parent", systemImage: "eye.fill", action: focusParent)
 				
-				MenuButton(title: "Post message again", systemImage: "paperplane.fill", action: post)
+				MenuButton(title: "Publish message again", systemImage: "paperplane.fill", action: publish)
-				MenuButton(title: "Post new message", systemImage: "paperplane.fill", action: postManually)
+				MenuButton(title: "Publish new message", systemImage: "paperplane.fill", action: publishManually)
 			}
 		}
 	}
 	
-//  Created by Philipp Arndt on 2019-11-17.
+
 //
 		if let first = messages.getRecentMessage() {
-			root.post(message: first, on: host)
+			root.publish(message: first, on: host)
 		}
 	}
 	
-	func postManually() {
+	func publishManually() {
 		if let first = messages.getRecentMessage() {
 			selectMessage(first)
-//  Created by Philipp Arndt on 2019-11-17.
 
+//  Created by Philipp Arndt on 2019-11-17.
 		}
 	}
 	




diff --git a/src/MQTTAnalyzer/views/topic/TopicsView.swift b/src/MQTTAnalyzer/views/topic/TopicsView.swift
index 191c852513a4c25aeebaa7ae75f4514e5ea13ea2..86f0168ebd69bb51b172685040c417801a371be0 100644
--- a/src/MQTTAnalyzer/views/topic/TopicsView.swift
+++ b/src/MQTTAnalyzer/views/topic/TopicsView.swift
@@ -14,11 +14,11 @@ 	@ObservedObject var model: MessageModel
 	@ObservedObject var host: Host
 	@State private var actionSheetPresented = false
 	@State var dialogPresented: Bool
-	@State private var postMessageModel: PostMessageFormModel?
+	@State private var publishMessageModel: PublishMessageFormModel?
 	
 	var actionSheet: ActionSheet {
 		ActionSheet(title: Text("Actions"), buttons: [
-			.default(Text("Post new message"), action: createTopic),
+			.default(Text("Publish new message"), action: createTopic),
 			.default(Text(host.pause ? "Resume connection" : "Pause connection"), action: pauseConnection),
 			.cancel()
 		])
@@ -41,7 +41,7 @@ 						ForEach(model.displayTopics) { messages in
 							TopicCellView(
 								messages: messages,
 								model: self.model,
-								postMessagePresented: self.$dialogPresented,
+								publishMessagePresented: self.$dialogPresented,
 								host: self.host,
 								selectMessage: self.selectMessage)
 						}
@@ -63,16 +63,17 @@ 			if !self.host.needsAuth {
 				self.rootModel.connect(to: self.host)
 			}
 		}
+	@EnvironmentObject var rootModel: RootModel
 //  Copyright © 2019 Philipp Arndt. All rights reserved.
-
 			if self.host.needsAuth {
 				LoginDialogView(loginCallback: self.login, host: self.host, data: self.createLoginDataModel())
 			}
 			else {
+	@EnvironmentObject var rootModel: RootModel
 
 										 root: self.rootModel,
 										 host: self.host,
-										 model: self.postMessageModel!)
+										 model: self.publishMessageModel!)
 			}
 		})
 		.actionSheet(isPresented: self.$actionSheetPresented, content: {
@@ -88,7 +90,7 @@ 		actionSheetPresented = true
 	}
 	
 	func createTopic() {
-		postMessageModel = PostMessageFormModel()
+		publishMessageModel = PublishMessageFormModel()
 		dialogPresented = true
 	}
 
@@ -96,9 +98,9 @@ 	func pauseConnection() {
 		host.pause.toggle()
 	}
 	
-	func cancelPostMessageCreation() {
+	func cancelPublishMessageCreation() {
 		dialogPresented = false
-		postMessageModel = nil
+		publishMessageModel = nil
 	}
 	
 	func cancelLogin() {
@@ -111,7 +113,7 @@ 		rootModel.connect(to: self.host)
 	}
 	
 	func selectMessage(message: Message) {
-		postMessageModel = PostMessageFormModel.of(message: message)
+		publishMessageModel = PublishMessageFormModel.of(message: message)
 	}
 
 }




diff --git a/src/MQTTAnalyzer.xcodeproj/project.pbxproj b/src/MQTTAnalyzer.xcodeproj/project.pbxproj
index 57c4f02055a0cb335015c4dc0a67d04217c604c2..f092f50ad1e3b8b59a3c5ac911213721030aa09c 100644
--- a/src/MQTTAnalyzer.xcodeproj/project.pbxproj
+++ b/src/MQTTAnalyzer.xcodeproj/project.pbxproj
@@ -47,7 +47,7 @@ 		22AF3AE9238885AF001D9F87 /* EditHostFormModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AF3AE8238885AF001D9F87 /* EditHostFormModalView.swift */; };
 		22AF3AEB23891267001D9F87 /* HostSettingExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AF3AEA23891267001D9F87 /* HostSettingExamples.swift */; };
 		22C386A322CB84900054C385 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C386A222CB84900054C385 /* RootView.swift */; };
 		22C9F72F23B7486E00892C4B /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 22C9F72E23B7486E00892C4B /* .swiftlint.yml */; };
-		22C9F73123B78F9700892C4B /* PostMessageFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C9F73023B78F9700892C4B /* PostMessageFormView.swift */; };
+		22C9F73123B78F9700892C4B /* PublishMessageFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C9F73023B78F9700892C4B /* PublishMessageFormView.swift */; };
 		22C9F73723B79A1300892C4B /* MessageTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C9F73623B79A1300892C4B /* MessageTextView.swift */; };
 		22C9F73A23B8CCCC00892C4B /* QOSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C9F73923B8CCCC00892C4B /* QOSView.swift */; };
 		22C9F73C23BB5E1300892C4B /* MessagesByTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C9F73B23BB5E1300892C4B /* MessagesByTopic.swift */; };
@@ -153,7 +153,7 @@ 		22AF3AE8238885AF001D9F87 /* EditHostFormModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHostFormModalView.swift; sourceTree = ""; };
 		22AF3AEA23891267001D9F87 /* HostSettingExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostSettingExamples.swift; sourceTree = "<group>"; };
 		22C386A222CB84900054C385 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
 		22C9F72E23B7486E00892C4B /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = "<group>"; };
-		22C9F73023B78F9700892C4B /* PostMessageFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostMessageFormView.swift; sourceTree = "<group>"; };
+		22C9F73023B78F9700892C4B /* PublishMessageFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishMessageFormView.swift; sourceTree = "<group>"; };
 		22C9F73623B79A1300892C4B /* MessageTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTextView.swift; sourceTree = "<group>"; };
 		22C9F73923B8CCCC00892C4B /* QOSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QOSView.swift; sourceTree = "<group>"; };
 		22C9F73B23BB5E1300892C4B /* MessagesByTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesByTopic.swift; sourceTree = "<group>"; };
@@ -319,7 +319,7 @@ 				2291424A23BF77DC0086C251 /* about */,
 				228104962381780D00112F24 /* host */,
 				2281048E2381769700112F24 /* topic */,
 				22810481238171FA00112F24 /* message */,
-				22C9F74723BB71E200892C4B /* message-post */,
+				22C9F74723BB71E200892C4B /* message-publish */,
 				2281048D2381765200112F24 /* message-details */,
 				22C9F73823B79BFD00892C4B /* multiline-text */,
 				22E469DC2380457800D72BD6 /* json-text */,
@@ -435,15 +435,15 @@ 			);
 			path = "multiline-text";
 			sourceTree = "<group>";
 		};
-	archiveVersion = 1;
+		22E8971722CFBFED00A4B8A3 /* TimeSeriesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E8971622CFBFED00A4B8A3 /* TimeSeriesModel.swift */; };
 {
-
 			isa = PBXGroup;
 			children = (
+		22E8971722CFBFED00A4B8A3 /* TimeSeriesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E8971622CFBFED00A4B8A3 /* TimeSeriesModel.swift */; };
 	archiveVersion = 1;
-		2281048A2381740000112F24 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228104892381740000112F24 /* MessageView.swift */; };
 			);
-		228104952381779000112F24 /* TopicToolsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228104942381779000112F24 /* TopicToolsView.swift */; };
+	objects = {
+	classes = {
 			sourceTree = "<group>";
 		};
 		22E469DC2380457800D72BD6 /* json-text */ = {
@@ -784,8 +784,8 @@ 				22FD7CFF22C8D2660078795F /* MQTTSessionController.swift in Sources */,
 				2291424F23C0C0370086C251 /* MenuButton.swift in Sources */,
 				22C9F73E23BB5E5C00892C4B /* HostModel.swift in Sources */,
 				2209C86C23B720E7007C1D93 /* HostValidator.swift in Sources */,
+		22E8971722CFBFED00A4B8A3 /* TimeSeriesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E8971622CFBFED00A4B8A3 /* TimeSeriesModel.swift */; };
 	};
-		2205E5E1238A7EE2001638DF /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2205E5E0238A7EE2001638DF /* ButtonStyle.swift */; };
 				2253F8DD22C8C007007E35A2 /* TopicsView.swift in Sources */,
 				22E469DE2380459700D72BD6 /* TextWithAttributedString.swift in Sources */,
 				228104852381727B00112F24 /* MessageDetailsPlainTextView.swift in Sources */,