Kaynağa Gözat

initial self-contained DcCore framework

cyberta 5 yıl önce
ebeveyn
işleme
8806ceb15b
30 değiştirilmiş dosya ile 3159 ekleme ve 0 silme
  1. 659 0
      DcCore/DcCore.xcodeproj/project.pbxproj
  2. 7 0
      DcCore/DcCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  3. 8 0
      DcCore/DcCore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  4. 8 0
      DcCore/DcCore/DC/Logger.swift
  5. 1244 0
      DcCore/DcCore/DC/Wrapper.swift
  6. 191 0
      DcCore/DcCore/DC/events.swift
  7. 9 0
      DcCore/DcCore/DC/wrapper.c
  8. 18 0
      DcCore/DcCore/DC/wrapper.h
  9. 21 0
      DcCore/DcCore/DcCore.h
  10. 23 0
      DcCore/DcCore/Extensions/String+Extensions.swift
  11. 72 0
      DcCore/DcCore/Extensions/UIColor+Extensions.swift
  12. 15 0
      DcCore/DcCore/Extensions/UIImage+Extension.swift
  13. 69 0
      DcCore/DcCore/Helper/Colors.swift
  14. 30 0
      DcCore/DcCore/Helper/Constants.swift
  15. 217 0
      DcCore/DcCore/Helper/Utils.swift
  16. 22 0
      DcCore/DcCore/Info.plist
  17. 83 0
      DcCore/DcCore/MessageKit/MessageKind.swift
  18. 42 0
      DcCore/DcCore/MessageKit/Protocols/AudioItem.swift
  19. 41 0
      DcCore/DcCore/MessageKit/Protocols/ContactItem.swift
  20. 37 0
      DcCore/DcCore/MessageKit/Protocols/LocationItem.swift
  21. 45 0
      DcCore/DcCore/MessageKit/Protocols/MediaItem.swift
  22. 43 0
      DcCore/DcCore/MessageKit/Protocols/MessageType.swift
  23. 38 0
      DcCore/DcCore/MessageKit/Protocols/SenderType.swift
  24. 57 0
      DcCore/DcCore/MessageKit/Sender.swift
  25. 19 0
      DcCore/DcCore/Model/Audio.swift
  26. 14 0
      DcCore/DcCore/Model/Location.swift
  27. 26 0
      DcCore/DcCore/Model/Media.swift
  28. 45 0
      DcCore/DcCore/Model/Message.swift
  29. 34 0
      DcCore/DcCoreTests/DcCoreTests.swift
  30. 22 0
      DcCore/DcCoreTests/Info.plist

+ 659 - 0
DcCore/DcCore.xcodeproj/project.pbxproj

@@ -0,0 +1,659 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 52;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		3042193B243DE0F200516852 /* DcCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30421931243DE0F200516852 /* DcCore.framework */; };
+		30421940243DE0F200516852 /* DcCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042193F243DE0F200516852 /* DcCoreTests.swift */; };
+		30421942243DE0F200516852 /* DcCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 30421934243DE0F200516852 /* DcCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		30421950243DE15D00516852 /* wrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 3042194C243DE15D00516852 /* wrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		30421951243DE15D00516852 /* Wrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042194D243DE15D00516852 /* Wrapper.swift */; };
+		30421952243DE15D00516852 /* wrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = 3042194E243DE15D00516852 /* wrapper.c */; };
+		30421959243DE6AD00516852 /* libdeltachat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 30421958243DE61400516852 /* libdeltachat.a */; platformFilter = ios; };
+		3042195B243DF1C500516852 /* deltachat.h in Headers */ = {isa = PBXBuildFile; fileRef = 3042195A243DF1C500516852 /* deltachat.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		3042195D243E23F100516852 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042195C243E23F100516852 /* Utils.swift */; };
+		30421960243E257100516852 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042195F243E257100516852 /* UIColor+Extensions.swift */; };
+		30421962243E26C800516852 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421961243E26C800516852 /* Logger.swift */; };
+		30421964243F0B8400516852 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421963243F0B8400516852 /* String+Extensions.swift */; };
+		30421967243F165C00516852 /* SenderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421966243F165C00516852 /* SenderType.swift */; };
+		30421969243F168700516852 /* Sender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421968243F168700516852 /* Sender.swift */; };
+		3042196B243F169B00516852 /* MessageKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042196A243F169B00516852 /* MessageKind.swift */; };
+		3042196D243F16E100516852 /* MessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042196C243F16E100516852 /* MessageType.swift */; };
+		3042196F243F176B00516852 /* MediaItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042196E243F176B00516852 /* MediaItem.swift */; };
+		30421971243F178800516852 /* AudioItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421970243F178800516852 /* AudioItem.swift */; };
+		30421973243F179000516852 /* ContactItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421972243F179000516852 /* ContactItem.swift */; };
+		30421975243F17F200516852 /* LocationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421974243F17F200516852 /* LocationItem.swift */; };
+		3042197D243F1CAA00516852 /* Audio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421979243F1CAA00516852 /* Audio.swift */; };
+		3042197E243F1CAA00516852 /* Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042197A243F1CAA00516852 /* Media.swift */; };
+		3042197F243F1CAA00516852 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042197B243F1CAA00516852 /* Location.swift */; };
+		30421980243F1CAA00516852 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3042197C243F1CAA00516852 /* Message.swift */; };
+		30421982243F1D5300516852 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421981243F1D5300516852 /* Colors.swift */; };
+		30421984243F1F0400516852 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421983243F1F0400516852 /* UIImage+Extension.swift */; };
+		30421986243F209E00516852 /* events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421985243F209E00516852 /* events.swift */; };
+		30421988243F23E500516852 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421987243F23E500516852 /* Constants.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		3042193C243DE0F200516852 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 30421928243DE0F100516852 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 30421930243DE0F200516852;
+			remoteInfo = DcCore;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		30421931243DE0F200516852 /* DcCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DcCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		30421934243DE0F200516852 /* DcCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DcCore.h; sourceTree = "<group>"; };
+		30421935243DE0F200516852 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		3042193A243DE0F200516852 /* DcCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DcCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		3042193F243DE0F200516852 /* DcCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DcCoreTests.swift; sourceTree = "<group>"; };
+		30421941243DE0F200516852 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		3042194C243DE15D00516852 /* wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wrapper.h; path = ../../DcCore/DcCore/DC/wrapper.h; sourceTree = "<group>"; };
+		3042194D243DE15D00516852 /* Wrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Wrapper.swift; path = ../../DcCore/DcCore/DC/Wrapper.swift; sourceTree = "<group>"; };
+		3042194E243DE15D00516852 /* wrapper.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wrapper.c; path = ../../DcCore/DcCore/DC/wrapper.c; sourceTree = "<group>"; };
+		30421958243DE61400516852 /* libdeltachat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdeltachat.a; path = "../deltachat-ios/libraries/deltachat-core-rust/target/universal/release/libdeltachat.a"; sourceTree = "<group>"; };
+		3042195A243DF1C500516852 /* deltachat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = deltachat.h; path = "../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi/deltachat.h"; sourceTree = "<group>"; };
+		3042195C243E23F100516852 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
+		3042195F243E257100516852 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
+		30421961243E26C800516852 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Logger.swift; path = ../../DcCore/DcCore/DC/Logger.swift; sourceTree = "<group>"; };
+		30421963243F0B8400516852 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
+		30421966243F165C00516852 /* SenderType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SenderType.swift; sourceTree = "<group>"; };
+		30421968243F168700516852 /* Sender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sender.swift; sourceTree = "<group>"; };
+		3042196A243F169B00516852 /* MessageKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageKind.swift; sourceTree = "<group>"; };
+		3042196C243F16E100516852 /* MessageType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageType.swift; sourceTree = "<group>"; };
+		3042196E243F176B00516852 /* MediaItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaItem.swift; sourceTree = "<group>"; };
+		30421970243F178800516852 /* AudioItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioItem.swift; sourceTree = "<group>"; };
+		30421972243F179000516852 /* ContactItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactItem.swift; sourceTree = "<group>"; };
+		30421974243F17F200516852 /* LocationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationItem.swift; sourceTree = "<group>"; };
+		30421979243F1CAA00516852 /* Audio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Audio.swift; sourceTree = "<group>"; };
+		3042197A243F1CAA00516852 /* Media.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Media.swift; sourceTree = "<group>"; };
+		3042197B243F1CAA00516852 /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
+		3042197C243F1CAA00516852 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
+		30421981243F1D5300516852 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
+		30421983243F1F0400516852 /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = "<group>"; };
+		30421985243F209E00516852 /* events.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = events.swift; path = ../../DcCore/DcCore/DC/events.swift; sourceTree = "<group>"; };
+		30421987243F23E500516852 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		3042192E243DE0F200516852 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				30421959243DE6AD00516852 /* libdeltachat.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		30421937243DE0F200516852 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				3042193B243DE0F200516852 /* DcCore.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		30421927243DE0F100516852 = {
+			isa = PBXGroup;
+			children = (
+				3042195A243DF1C500516852 /* deltachat.h */,
+				30421933243DE0F200516852 /* DcCore */,
+				3042193E243DE0F200516852 /* DcCoreTests */,
+				30421932243DE0F200516852 /* Products */,
+				30421957243DE61400516852 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		30421932243DE0F200516852 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				30421931243DE0F200516852 /* DcCore.framework */,
+				3042193A243DE0F200516852 /* DcCoreTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		30421933243DE0F200516852 /* DcCore */ = {
+			isa = PBXGroup;
+			children = (
+				30421978243F1C9900516852 /* Model */,
+				30421977243F1AF400516852 /* Helper */,
+				30421965243F12E900516852 /* MessageKit */,
+				3042195E243E255000516852 /* Extensions */,
+				3042194B243DE15D00516852 /* DC */,
+				30421934243DE0F200516852 /* DcCore.h */,
+				30421935243DE0F200516852 /* Info.plist */,
+			);
+			path = DcCore;
+			sourceTree = "<group>";
+		};
+		3042193E243DE0F200516852 /* DcCoreTests */ = {
+			isa = PBXGroup;
+			children = (
+				3042193F243DE0F200516852 /* DcCoreTests.swift */,
+				30421941243DE0F200516852 /* Info.plist */,
+			);
+			path = DcCoreTests;
+			sourceTree = "<group>";
+		};
+		3042194B243DE15D00516852 /* DC */ = {
+			isa = PBXGroup;
+			children = (
+				30421985243F209E00516852 /* events.swift */,
+				30421961243E26C800516852 /* Logger.swift */,
+				3042194E243DE15D00516852 /* wrapper.c */,
+				3042194C243DE15D00516852 /* wrapper.h */,
+				3042194D243DE15D00516852 /* Wrapper.swift */,
+			);
+			name = DC;
+			path = "../../deltachat-ios/DC";
+			sourceTree = "<group>";
+		};
+		30421957243DE61400516852 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				30421958243DE61400516852 /* libdeltachat.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		3042195E243E255000516852 /* Extensions */ = {
+			isa = PBXGroup;
+			children = (
+				3042195F243E257100516852 /* UIColor+Extensions.swift */,
+				30421963243F0B8400516852 /* String+Extensions.swift */,
+				30421983243F1F0400516852 /* UIImage+Extension.swift */,
+			);
+			path = Extensions;
+			sourceTree = "<group>";
+		};
+		30421965243F12E900516852 /* MessageKit */ = {
+			isa = PBXGroup;
+			children = (
+				3042196A243F169B00516852 /* MessageKind.swift */,
+				30421968243F168700516852 /* Sender.swift */,
+				30421976243F17FF00516852 /* Protocols */,
+			);
+			path = MessageKit;
+			sourceTree = "<group>";
+		};
+		30421976243F17FF00516852 /* Protocols */ = {
+			isa = PBXGroup;
+			children = (
+				30421970243F178800516852 /* AudioItem.swift */,
+				30421972243F179000516852 /* ContactItem.swift */,
+				30421974243F17F200516852 /* LocationItem.swift */,
+				3042196E243F176B00516852 /* MediaItem.swift */,
+				3042196C243F16E100516852 /* MessageType.swift */,
+				30421966243F165C00516852 /* SenderType.swift */,
+			);
+			path = Protocols;
+			sourceTree = "<group>";
+		};
+		30421977243F1AF400516852 /* Helper */ = {
+			isa = PBXGroup;
+			children = (
+				30421987243F23E500516852 /* Constants.swift */,
+				30421981243F1D5300516852 /* Colors.swift */,
+				3042195C243E23F100516852 /* Utils.swift */,
+			);
+			path = Helper;
+			sourceTree = "<group>";
+		};
+		30421978243F1C9900516852 /* Model */ = {
+			isa = PBXGroup;
+			children = (
+				30421979243F1CAA00516852 /* Audio.swift */,
+				3042197B243F1CAA00516852 /* Location.swift */,
+				3042197A243F1CAA00516852 /* Media.swift */,
+				3042197C243F1CAA00516852 /* Message.swift */,
+			);
+			path = Model;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		3042192C243DE0F200516852 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				30421950243DE15D00516852 /* wrapper.h in Headers */,
+				30421942243DE0F200516852 /* DcCore.h in Headers */,
+				3042195B243DF1C500516852 /* deltachat.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		30421930243DE0F200516852 /* DcCore */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 30421945243DE0F200516852 /* Build configuration list for PBXNativeTarget "DcCore" */;
+			buildPhases = (
+				30421954243DE38E00516852 /* ShellScript */,
+				3042192C243DE0F200516852 /* Headers */,
+				3042192D243DE0F200516852 /* Sources */,
+				3042192E243DE0F200516852 /* Frameworks */,
+				3042192F243DE0F200516852 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = DcCore;
+			productName = DcCore;
+			productReference = 30421931243DE0F200516852 /* DcCore.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		30421939243DE0F200516852 /* DcCoreTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 30421948243DE0F200516852 /* Build configuration list for PBXNativeTarget "DcCoreTests" */;
+			buildPhases = (
+				30421936243DE0F200516852 /* Sources */,
+				30421937243DE0F200516852 /* Frameworks */,
+				30421938243DE0F200516852 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				3042193D243DE0F200516852 /* PBXTargetDependency */,
+			);
+			name = DcCoreTests;
+			productName = DcCoreTests;
+			productReference = 3042193A243DE0F200516852 /* DcCoreTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		30421928243DE0F100516852 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 1140;
+				LastUpgradeCheck = 1140;
+				ORGANIZATIONNAME = "merlinux GmbH";
+				TargetAttributes = {
+					30421930243DE0F200516852 = {
+						CreatedOnToolsVersion = 11.4;
+					};
+					30421939243DE0F200516852 = {
+						CreatedOnToolsVersion = 11.4;
+					};
+				};
+			};
+			buildConfigurationList = 3042192B243DE0F100516852 /* Build configuration list for PBXProject "DcCore" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 30421927243DE0F100516852;
+			productRefGroup = 30421932243DE0F200516852 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				30421930243DE0F200516852 /* DcCore */,
+				30421939243DE0F200516852 /* DcCoreTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		3042192F243DE0F200516852 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		30421938243DE0F200516852 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		30421954243DE38E00516852 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+			);
+			outputFileListPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "# The $PATH used by XCode likely won't contain Cargo, fix that.\n# This assumes a default `rustup` setup.\nexport PATH=\"$HOME/.cargo/bin:$PATH\"\n\nexport CFLAGS_x86_64_apple_darwin=\"-I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include\"\n\n# ensure all targets are installed\nrustup target add aarch64-apple-ios x86_64-apple-ios --toolchain `cat ../deltachat-ios/libraries/deltachat-core-rust/rust-toolchain`\n\n# --xcode-integ determines --release and --targets from XCode's env vars.\n# Depending your setup, specify the rustup toolchain explicitly.\ncargo +`cat ../deltachat-ios/libraries/deltachat-core-rust/rust-toolchain` lipo --release --manifest-path ../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi/Cargo.toml --no-default-features --features nightly\n";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		3042192D243DE0F200516852 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				30421969243F168700516852 /* Sender.swift in Sources */,
+				30421988243F23E500516852 /* Constants.swift in Sources */,
+				30421973243F179000516852 /* ContactItem.swift in Sources */,
+				30421962243E26C800516852 /* Logger.swift in Sources */,
+				30421986243F209E00516852 /* events.swift in Sources */,
+				30421980243F1CAA00516852 /* Message.swift in Sources */,
+				30421951243DE15D00516852 /* Wrapper.swift in Sources */,
+				30421971243F178800516852 /* AudioItem.swift in Sources */,
+				3042197F243F1CAA00516852 /* Location.swift in Sources */,
+				30421984243F1F0400516852 /* UIImage+Extension.swift in Sources */,
+				3042196D243F16E100516852 /* MessageType.swift in Sources */,
+				3042197E243F1CAA00516852 /* Media.swift in Sources */,
+				3042196B243F169B00516852 /* MessageKind.swift in Sources */,
+				30421952243DE15D00516852 /* wrapper.c in Sources */,
+				30421967243F165C00516852 /* SenderType.swift in Sources */,
+				30421982243F1D5300516852 /* Colors.swift in Sources */,
+				3042197D243F1CAA00516852 /* Audio.swift in Sources */,
+				3042195D243E23F100516852 /* Utils.swift in Sources */,
+				30421964243F0B8400516852 /* String+Extensions.swift in Sources */,
+				30421960243E257100516852 /* UIColor+Extensions.swift in Sources */,
+				3042196F243F176B00516852 /* MediaItem.swift in Sources */,
+				30421975243F17F200516852 /* LocationItem.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		30421936243DE0F200516852 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				30421940243DE0F200516852 /* DcCoreTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		3042193D243DE0F200516852 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 30421930243DE0F200516852 /* DcCore */;
+			targetProxy = 3042193C243DE0F200516852 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		30421943243DE0F200516852 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.4;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		30421944243DE0F200516852 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.4;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				MTL_FAST_MATH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		30421946243DE0F200516852 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = 8Y86453UA8;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi",
+				);
+				INFOPLIST_FILE = DcCore/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/target/universal/release",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/target/universal/debug",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcCore;
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SKIP_INSTALL = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_VERSION = 4.2;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		30421947243DE0F200516852 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = 8Y86453UA8;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi",
+				);
+				INFOPLIST_FILE = DcCore/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/target/universal/release",
+					"$(PROJECT_DIR)/../deltachat-ios/libraries/deltachat-core-rust/target/universal/debug",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcCore;
+				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+				SKIP_INSTALL = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_VERSION = 4.2;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+		30421949243DE0F200516852 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = 8Y86453UA8;
+				INFOPLIST_FILE = DcCoreTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcCoreTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		3042194A243DE0F200516852 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = 8Y86453UA8;
+				INFOPLIST_FILE = DcCoreTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcCoreTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		3042192B243DE0F100516852 /* Build configuration list for PBXProject "DcCore" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				30421943243DE0F200516852 /* Debug */,
+				30421944243DE0F200516852 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		30421945243DE0F200516852 /* Build configuration list for PBXNativeTarget "DcCore" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				30421946243DE0F200516852 /* Debug */,
+				30421947243DE0F200516852 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		30421948243DE0F200516852 /* Build configuration list for PBXNativeTarget "DcCoreTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				30421949243DE0F200516852 /* Debug */,
+				3042194A243DE0F200516852 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 30421928243DE0F100516852 /* Project object */;
+}

+ 7 - 0
DcCore/DcCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:DcCore.xcodeproj">
+   </FileRef>
+</Workspace>

+ 8 - 0
DcCore/DcCore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?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>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 8 - 0
DcCore/DcCore/DC/Logger.swift

@@ -0,0 +1,8 @@
+import Foundation
+public protocol Logger {
+    func verbose(_ message: String)
+    func debug(_ message: String)
+    func info(_ message: String)
+    func warning(_ message: String)
+    func error(_ message: String)
+}

+ 1244 - 0
DcCore/DcCore/DC/Wrapper.swift

@@ -0,0 +1,1244 @@
+import Foundation
+import UIKit
+import AVFoundation
+
+class DcContext {
+    /// TODO: THIS global instance should be replaced in the future, for example for a multi-account scenario,
+    /// where we want to have more than one DcContext.
+    static let dcContext: DcContext = DcContext()
+    var logger: Logger?
+    let contextPointer: OpaquePointer
+    var lastErrorString: String?
+
+    private init() {
+        var version = ""
+        if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+            version += " " + appVersion
+        }
+
+        contextPointer = dc_context_new(callback_ios, nil, "iOS" + version)
+    }
+
+    deinit {
+        dc_context_unref(contextPointer)
+    }
+
+    /// Injection of DcContext is preferred over the usage of the shared variable
+    static var shared: DcContext {
+        return .dcContext
+    }
+
+    func createContact(name: String, email: String) -> Int {
+        return Int(dc_create_contact(contextPointer, name, email))
+    }
+
+    func deleteContact(contactId: Int) -> Bool {
+        return dc_delete_contact(self.contextPointer, UInt32(contactId)) == 1
+    }
+
+    func getContacts(flags: Int32, queryString: String? = nil) -> [Int] {
+        let cContacts = dc_get_contacts(contextPointer, UInt32(flags), queryString)
+        return Utils.copyAndFreeArray(inputArray: cContacts)
+    }
+
+    func getBlockedContacts() -> [Int] {
+        let cBlockedContacts = dc_get_blocked_contacts(contextPointer)
+        return Utils.copyAndFreeArray(inputArray: cBlockedContacts)
+    }
+
+    func addContacts(contactString: String) {
+        dc_add_address_book(contextPointer, contactString)
+    }
+
+    func getChat(chatId: Int) -> DcChat {
+        return DcChat(id: chatId)
+    }
+
+    func getChatIdByContactId(_ contactId: Int) -> Int? {
+        let chatId = dc_get_chat_id_by_contact_id(contextPointer, UInt32(contactId))
+        if chatId == 0 {
+            return nil
+        } else {
+            return Int(chatId)
+        }
+    }
+
+    func createChatByMessageId(_ messageId: Int) -> DcChat {
+        let chatId = dc_create_chat_by_msg_id(contextPointer, UInt32(messageId))
+        return DcChat(id: Int(chatId))
+    }
+
+    func getChatlist(flags: Int32, queryString: String?, queryId: Int) -> DcChatlist {
+        let chatlistPointer = dc_get_chatlist(contextPointer, flags, queryString, UInt32(queryId))
+        let chatlist = DcChatlist(chatListPointer: chatlistPointer)
+        return chatlist
+    }
+
+    func getChatMedia(chatId: Int, messageType: Int32, messageType2: Int32, messageType3: Int32) -> [Int] {
+        guard let messagesPointer = dc_get_chat_media(contextPointer, UInt32(chatId), messageType, messageType2, messageType3) else {
+            return []
+        }
+
+        let messageIds: [Int] =  Utils.copyAndFreeArray(inputArray: messagesPointer)
+        return messageIds
+    }
+
+    @discardableResult
+    func createChatByContactId(contactId: Int) -> Int {
+        return Int(dc_create_chat_by_contact_id(contextPointer, UInt32(contactId)))
+    }
+
+    func getChatIdByContactId(contactId: Int) -> Int {
+        return Int(dc_get_chat_id_by_contact_id(contextPointer, UInt32(contactId)))
+    }
+
+    func createGroupChat(verified: Bool, name: String) -> Int {
+        return Int(dc_create_group_chat(contextPointer, verified ? 1 : 0, name))
+    }
+
+    func addContactToChat(chatId: Int, contactId: Int) -> Bool {
+        return dc_add_contact_to_chat(contextPointer, UInt32(chatId), UInt32(contactId)) == 1
+    }
+
+    func removeContactFromChat(chatId: Int, contactId: Int) -> Bool {
+        return dc_remove_contact_from_chat(contextPointer, UInt32(chatId), UInt32(contactId)) == 1
+    }
+
+    func setChatName(chatId: Int, name: String) -> Bool {
+        return dc_set_chat_name(contextPointer, UInt32(chatId), name) == 1
+    }
+
+    func deleteChat(chatId: Int) {
+        dc_delete_chat(contextPointer, UInt32(chatId))
+    }
+
+    func archiveChat(chatId: Int, archive: Bool) {
+        dc_set_chat_visibility(contextPointer, UInt32(chatId), Int32(archive ? DC_CHAT_VISIBILITY_ARCHIVED : DC_CHAT_VISIBILITY_NORMAL))
+    }
+
+    func setChatVisibility(chatId: Int, visibility: Int32) {
+        dc_set_chat_visibility(contextPointer, UInt32(chatId), visibility)
+    }
+
+    func marknoticedChat(chatId: Int) {
+        dc_marknoticed_chat(self.contextPointer, UInt32(chatId))
+    }
+
+    func getSecurejoinQr (chatId: Int) -> String? {
+        if let cString = dc_get_securejoin_qr(self.contextPointer, UInt32(chatId)) {
+            let swiftString = String(cString: cString)
+            dc_str_unref(cString)
+            return swiftString
+        }
+        return nil
+    }
+
+    func joinSecurejoin (qrCode: String) -> Int {
+        return Int(dc_join_securejoin(contextPointer, qrCode))
+    }
+
+    func checkQR(qrCode: String) -> DcLot {
+        return DcLot(dc_check_qr(contextPointer, qrCode))
+    }
+
+    func stopOngoingProcess() {
+        dc_stop_ongoing_process(contextPointer)
+    }
+
+    func getInfo() -> [[String]] {
+        if let cString = dc_get_info(contextPointer) {
+            let info = String(cString: cString)
+            dc_str_unref(cString)
+            logger?.info(info)
+            return info.components(separatedBy: "\n").map { val in
+                val.components(separatedBy: "=")
+            }
+        }
+        return []
+    }
+
+    func interruptIdle() {
+        dc_interrupt_imap_idle(contextPointer)
+        dc_interrupt_smtp_idle((contextPointer))
+        dc_interrupt_mvbox_idle((contextPointer))
+        dc_interrupt_sentbox_idle((contextPointer))
+    }
+
+    func openDatabase(dbFile: String) {
+        _ = dc_open(contextPointer, dbFile, nil)
+    }
+
+    func closeDatabase() {
+        dc_close(contextPointer)
+    }
+
+    func performImap() {
+        dc_perform_imap_jobs(contextPointer)
+        dc_perform_imap_fetch(contextPointer)
+        dc_perform_imap_idle(contextPointer)
+    }
+
+    func performMoveBox() {
+        dc_perform_mvbox_jobs(contextPointer)
+        dc_perform_mvbox_fetch(contextPointer)
+        dc_perform_mvbox_idle(contextPointer)
+    }
+
+    func performSmtp() {
+        dc_perform_smtp_jobs(contextPointer)
+        dc_perform_smtp_idle(contextPointer)
+    }
+
+    func performSentbox() {
+        dc_perform_sentbox_jobs(contextPointer)
+        dc_perform_sentbox_fetch(contextPointer)
+        dc_perform_sentbox_idle(contextPointer)
+    }
+
+    func setStockTranslation(id: Int32, localizationKey: String) {
+        dc_set_stock_translation(contextPointer, UInt32(id), String.localized(localizationKey))
+    }
+
+    func getDraft(chatId: Int) -> String? {
+        if let draft = dc_get_draft(contextPointer, UInt32(chatId)) {
+            if let cString = dc_msg_get_text(draft) {
+                let swiftString = String(cString: cString)
+                dc_str_unref(cString)
+                dc_msg_unref(draft)
+                return swiftString
+            }
+            dc_msg_unref(draft)
+            return nil
+        }
+        return nil
+    }
+
+    func setDraft(chatId: Int, draftText: String) {
+        let draft = dc_msg_new(contextPointer, DC_MSG_TEXT)
+        dc_msg_set_text(draft, draftText.cString(using: .utf8))
+        dc_set_draft(contextPointer, UInt32(chatId), draft)
+
+        // cleanup
+        dc_msg_unref(draft)
+    }
+
+    func getFreshMessages() -> DcArray {
+        return DcArray(arrayPointer: dc_get_fresh_msgs(contextPointer))
+    }
+
+    func markSeenMessages(messageIds: [UInt32], count: Int = 1) {
+        let ptr = UnsafePointer(messageIds)
+        dc_markseen_msgs(contextPointer, ptr, Int32(count))
+    }
+
+    func getChatMessages(chatId: Int) -> OpaquePointer {
+        return dc_get_chat_msgs(contextPointer, UInt32(chatId), 0, 0)
+    }
+    
+    func getMsgInfo(msgId: Int) -> String {
+        if let cString = dc_get_msg_info(self.contextPointer, UInt32(msgId)) {
+            let swiftString = String(cString: cString)
+            dc_str_unref(cString)
+            return swiftString
+        }
+        return "ErrGetMsgInfo"
+    }
+
+    func deleteMessage(msgId: Int) {
+        dc_delete_msgs(contextPointer, [UInt32(msgId)], 1)
+    }
+
+    func forwardMessage(with msgId: Int, to chat: Int) {
+        dc_forward_msgs(contextPointer, [UInt32(msgId)], 1, UInt32(chat))
+    }
+
+    func sendTextInChat(id: Int, message: String) {
+        dc_send_text_msg(contextPointer, UInt32(id), message)
+    }
+
+    func initiateKeyTransfer() -> String? {
+        if let cString = dc_initiate_key_transfer(self.contextPointer) {
+            let swiftString = String(cString: cString)
+            dc_str_unref(cString)
+            return swiftString
+        }
+        return nil
+    }
+
+    func continueKeyTransfer(msgId: Int, setupCode: String) -> Bool {
+        return dc_continue_key_transfer(self.contextPointer, UInt32(msgId), setupCode) != 0
+    }
+
+    func configure() {
+        dc_configure(contextPointer)
+    }
+
+    func getConfig(_ key: String) -> String? {
+        guard let cString = dc_get_config(self.contextPointer, key) else { return nil }
+        let value = String(cString: cString)
+        dc_str_unref(cString)
+        if value.isEmpty {
+            return nil
+        }
+        return value
+    }
+
+    func setConfig(_ key: String, _ value: String?) {
+        if let v = value {
+            dc_set_config(self.contextPointer, key, v)
+        } else {
+            dc_set_config(self.contextPointer, key, nil)
+        }
+    }
+
+    func getConfigBool(_ key: String) -> Bool {
+        return strToBool(getConfig(key))
+    }
+
+    func setConfigBool(_ key: String, _ value: Bool) {
+        let vStr = value ? "1" : "0"
+        setConfig(key, vStr)
+    }
+
+    func getConfigInt(_ key: String) -> Int {
+        let vStr = getConfig(key)
+        if vStr == nil {
+            return 0
+        }
+        let vInt = Int(vStr!)
+        if vInt == nil {
+            return 0
+        }
+        return vInt!
+    }
+
+    private func setConfigInt(_ key: String, _ value: Int) {
+        setConfig(key, String(value))
+    }
+
+    func getUnreadMessages(chatId: Int) -> Int {
+        return Int(dc_get_fresh_msg_cnt(contextPointer, UInt32(chatId)))
+    }
+
+    func emptyServer(flags: Int) {
+        dc_empty_server(contextPointer, UInt32(flags))
+    }
+
+    func isConfigured() -> Bool {
+        return dc_is_configured(contextPointer) != 0
+    }
+
+    func getSelfAvatarImage() -> UIImage? {
+       guard let fileName = selfavatar else { return nil }
+       let path: URL = URL(fileURLWithPath: fileName, isDirectory: false)
+       if path.isFileURL {
+           do {
+               let data = try Data(contentsOf: path)
+               return UIImage(data: data)
+           } catch {
+               logger?.warning("failed to load image: \(fileName), \(error)")
+               return nil
+           }
+       }
+       return nil
+    }
+
+    func saveChatAvatarImage(chatId: Int, path: String) {
+        dc_set_chat_profile_image(contextPointer, UInt32(chatId), path)
+    }
+
+    @discardableResult
+    func addDeviceMessage(label: String, msg: DcMsg) -> Int {
+        return Int(dc_add_device_msg(contextPointer, label.cString(using: .utf8), msg.cptr))
+    }
+
+    func updateDeviceChats() {
+        dc_update_device_chats(contextPointer)
+    }
+
+    func getProviderFromEmail(addr: String) -> DcProvider? {
+        guard let dcProviderPointer = dc_provider_new_from_email(contextPointer, addr) else { return nil }
+        return DcProvider(dcProviderPointer)
+    }
+
+    func imex(what: Int32, directory: String) {
+        dc_imex(contextPointer, what, directory, nil)
+    }
+
+    func imexHasBackup(filePath: String) -> String? {
+        var file: String?
+        if let cString = dc_imex_has_backup(contextPointer, filePath) {
+            file = String(cString: cString)
+            dc_str_unref(cString)
+        }
+        return file
+    }
+
+    func isSendingLocationsToChat(chatId: Int) -> Bool {
+        return dc_is_sending_locations_to_chat(contextPointer, UInt32(chatId)) == 1
+    }
+
+    func sendLocationsToChat(chatId: Int, seconds: Int) {
+        dc_send_locations_to_chat(contextPointer, UInt32(chatId), Int32(seconds))
+    }
+
+    func setLocation(latitude: Double, longitude: Double, accuracy: Double) {
+        dc_set_location(contextPointer, latitude, longitude, accuracy)
+    }
+
+    func searchMessages(chatId: Int = 0, searchText: String) -> [Int] {
+        guard let arrayPointer = dc_search_msgs(contextPointer, UInt32(chatId), searchText) else {
+            return []
+        }
+        let messageIds = Utils.copyAndFreeArray(inputArray: arrayPointer)
+        return messageIds
+    }
+
+    // call dc_maybe_network() from a worker thread.
+    func maybeNetwork() {
+        dc_maybe_network(contextPointer)
+    }
+
+    // also, there is no much worth in adding a separate function or so
+    // for each config option - esp. if they are just forwarded to the core
+    // and set/get only at one line of code each.
+    // this adds a complexity that can be avoided -
+    // and makes grep harder as these names are typically named following different guidelines.
+
+    var displayname: String? {
+        set { setConfig("displayname", newValue) }
+        get { return getConfig("displayname") }
+    }
+
+    var selfstatus: String? {
+        set { setConfig("selfstatus", newValue) }
+        get { return getConfig("selfstatus") }
+    }
+
+    var selfavatar: String? {
+        set { setConfig("selfavatar", newValue) }
+        get { return getConfig("selfavatar") }
+    }
+
+    var addr: String? {
+        set { setConfig("addr", newValue) }
+        get { return getConfig("addr") }
+    }
+
+    var mailServer: String? {
+        set { setConfig("mail_server", newValue) }
+        get { return getConfig("mail_server") }
+    }
+
+    var mailUser: String? {
+        set { setConfig("mail_user", newValue) }
+        get { return getConfig("mail_user") }
+    }
+
+    var mailPw: String? {
+        set { setConfig("mail_pw", newValue) }
+        get { return getConfig("mail_pw") }
+    }
+
+    var mailPort: String? {
+        set { setConfig("mail_port", newValue) }
+        get { return getConfig("mail_port") }
+    }
+
+    var sendServer: String? {
+        set { setConfig("send_server", newValue) }
+        get { return getConfig("send_server") }
+    }
+
+    var sendUser: String? {
+        set { setConfig("send_user", newValue) }
+        get { return getConfig("send_user") }
+    }
+
+    var sendPw: String? {
+        set { setConfig("send_pw", newValue) }
+        get { return getConfig("send_pw") }
+    }
+
+    var sendPort: String? {
+        set { setConfig("send_port", newValue) }
+        get { return getConfig("send_port") }
+    }
+
+    var certificateChecks: Int {
+        set {
+            setConfig("smtp_certificate_checks", "\(newValue)")
+            setConfig("imap_certificate_checks", "\(newValue)")
+        }
+        get {
+            if let str = getConfig("imap_certificate_checks") {
+                return Int(str) ?? 0
+            } else {
+                return 0
+            }
+        }
+    }
+
+    private var serverFlags: Int {
+        // IMAP-/SMTP-flags as a combination of DC_LP flags
+        set {
+            setConfig("server_flags", "\(newValue)")
+        }
+        get {
+            if let str = getConfig("server_flags") {
+                return Int(str) ?? 0
+            } else {
+                return 0
+            }
+        }
+    }
+
+    func setImapSecurity(imapFlags flags: Int) {
+        var sf = serverFlags
+        sf = sf & ~0x700 // DC_LP_IMAP_SOCKET_FLAGS
+        sf = sf | flags
+        serverFlags = sf
+    }
+
+    func setSmtpSecurity(smptpFlags flags: Int) {
+        var sf = serverFlags
+        sf = sf & ~0x70000 // DC_LP_SMTP_SOCKET_FLAGS
+        sf = sf | flags
+        serverFlags = sf
+    }
+
+    func setAuthFlags(flags: Int) {
+        var sf = serverFlags
+        sf = sf & ~0x6 // DC_LP_AUTH_FLAGS
+        sf = sf | flags
+        serverFlags = sf
+    }
+
+    func getImapSecurity() -> Int {
+        var sf = serverFlags
+        sf = sf & 0x700 // DC_LP_IMAP_SOCKET_FLAGS
+        return sf
+    }
+
+    func getSmtpSecurity() -> Int {
+        var sf = serverFlags
+        sf = sf & 0x70000  // DC_LP_SMTP_SOCKET_FLAGS
+        return sf
+    }
+
+    func getAuthFlags() -> Int {
+        var sf = serverFlags
+        sf = sf & 0x6 // DC_LP_AUTH_FLAGS
+        return sf
+    }
+
+    var e2eeEnabled: Bool {
+        set { setConfigBool("e2ee_enabled", newValue) }
+        get { return getConfigBool("e2ee_enabled") }
+    }
+
+    var mdnsEnabled: Bool {
+        set { setConfigBool("mdns_enabled", newValue) }
+        get { return getConfigBool("mdns_enabled") }
+    }
+
+    var showEmails: Int {
+        // one of DC_SHOW_EMAILS_*
+        set { setConfigInt("show_emails", newValue) }
+        get { return getConfigInt("show_emails") }
+    }
+
+    // do not use. use DcContext::isConfigured() instead
+    var configured: Bool {
+        return getConfigBool("configured")
+    }
+}
+
+class DcChatlist {
+    private var chatListPointer: OpaquePointer?
+
+    // takes ownership of specified pointer
+    init(chatListPointer: OpaquePointer?) {
+        self.chatListPointer = chatListPointer
+    }
+
+    deinit {
+        dc_chatlist_unref(chatListPointer)
+    }
+
+    var length: Int {
+        return dc_chatlist_get_cnt(chatListPointer)
+    }
+
+    func getChatId(index: Int) -> Int {
+        return Int(dc_chatlist_get_chat_id(chatListPointer, index))
+    }
+
+    func getMsgId(index: Int) -> Int {
+        return Int(dc_chatlist_get_msg_id(chatListPointer, index))
+    }
+
+    func getSummary(index: Int) -> DcLot {
+        guard let lotPointer = dc_chatlist_get_summary(self.chatListPointer, index, nil) else {
+            fatalError("lot-pointer was nil")
+        }
+        return DcLot(lotPointer)
+    }
+}
+
+class DcChat {
+    var chatPointer: OpaquePointer?
+
+    // use DcContext.getChat() instead of calling the constructor directly
+    init(id: Int) {
+        if let p = dc_get_chat(DcContext.shared.contextPointer, UInt32(id)) {
+            chatPointer = p
+        } else {
+            fatalError("Invalid chatID opened \(id)")
+        }
+    }
+
+    deinit {
+        dc_chat_unref(chatPointer)
+    }
+
+    var id: Int {
+        return Int(dc_chat_get_id(chatPointer))
+    }
+
+    var name: String {
+        guard let cString = dc_chat_get_name(chatPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var type: Int {
+        return Int(dc_chat_get_type(chatPointer))
+    }
+
+    var chatType: ChatType {
+        return ChatType(rawValue: type) ?? ChatType.GROUP // group as fallback - shouldn't get here
+    }
+
+    var color: UIColor {
+        return UIColor(netHex: Int(dc_chat_get_color(chatPointer)))
+    }
+
+    var isArchived: Bool {
+        return Int(dc_chat_get_visibility(chatPointer)) == DC_CHAT_VISIBILITY_ARCHIVED
+    }
+
+    var visibility: Int32 {
+        return dc_chat_get_visibility(chatPointer)
+    }
+
+    var isUnpromoted: Bool {
+        return Int(dc_chat_is_unpromoted(chatPointer)) != 0
+    }
+
+    var isGroup: Bool {
+        let type = Int(dc_chat_get_type(chatPointer))
+        return type == DC_CHAT_TYPE_GROUP || type == DC_CHAT_TYPE_VERIFIED_GROUP
+    }
+
+    var isSelfTalk: Bool {
+        return Int(dc_chat_is_self_talk(chatPointer)) != 0
+    }
+
+    var isDeviceTalk: Bool {
+        return Int(dc_chat_is_device_talk(chatPointer)) != 0
+    }
+
+    var canSend: Bool {
+        return Int(dc_chat_can_send(chatPointer)) != 0
+    }
+
+    var isVerified: Bool {
+        return dc_chat_is_verified(chatPointer) > 0
+    }
+
+    var contactIds: [Int] {
+        return Utils.copyAndFreeArray(inputArray: dc_get_chat_contacts(DcContext.shared.contextPointer, UInt32(id)))
+    }
+
+    lazy var profileImage: UIImage? = { [unowned self] in
+        guard let cString = dc_chat_get_profile_image(chatPointer) else { return nil }
+        let filename = String(cString: cString)
+        dc_str_unref(cString)
+        let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
+        if path.isFileURL {
+            do {
+                let data = try Data(contentsOf: path)
+                let image = UIImage(data: data)
+                return image
+            } catch {
+                DcContext.shared.logger?.warning("failed to load image: \(filename), \(error)")
+                return nil
+            }
+        }
+        return nil
+        }()
+
+    var isSendingLocations: Bool {
+        return dc_chat_is_sending_locations(chatPointer) == 1
+    }
+}
+
+class DcArray {
+    private var dcArrayPointer: OpaquePointer?
+
+    init(arrayPointer: OpaquePointer) {
+        dcArrayPointer = arrayPointer
+    }
+
+    deinit {
+        dc_array_unref(dcArrayPointer)
+    }
+
+    var count: Int {
+       return Int(dc_array_get_cnt(dcArrayPointer))
+    }
+
+    ///TODO: add missing methods here
+}
+
+class DcMsg: MessageType {
+    private var messagePointer: OpaquePointer?
+
+    /**
+        viewType: one of
+            DC_MSG_TEXT,
+            DC_MSG_IMAGE,
+            DC_MSG_GIF,
+            DC_MSG_STICKER,
+            DC_MSG_AUDIO,
+            DC_MSG_VOICE,
+            DC_MSG_VIDEO,
+            DC_MSG_FILE
+     */
+    init(viewType: Int32) {
+        messagePointer = dc_msg_new(DcContext.shared.contextPointer, viewType)
+    }
+
+    init(id: Int) {
+        messagePointer = dc_get_msg(DcContext.shared.contextPointer, UInt32(id))
+    }
+
+    init(type: Int32) {
+        messagePointer = dc_msg_new(DcContext.shared.contextPointer, type)
+    }
+
+    deinit {
+        dc_msg_unref(messagePointer)
+    }
+
+    var cptr: OpaquePointer? {
+        return messagePointer
+    }
+
+    lazy var sender: SenderType = {
+        Sender(id: "\(fromContactId)", displayName: fromContact.displayName)
+    }()
+
+    lazy var sentDate: Date = {
+        Date(timeIntervalSince1970: Double(timestamp))
+    }()
+
+    func formattedSentDate() -> String {
+        return DateUtils.getExtendedRelativeTimeSpanString(timeStamp: Double(timestamp))
+    }
+
+    lazy var kind: MessageKind = {
+        if isInfo {
+            let text = NSAttributedString(string: self.text ?? "", attributes: [
+                NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12),
+                NSAttributedString.Key.foregroundColor: DcColors.grayTextColor,
+                ])
+            return MessageKind.info(text)
+        } else if isSetupMessage {
+            return MessageKind.text(String.localized("autocrypt_asm_click_body"))
+        }
+
+        let text = self.text ?? ""
+
+        if self.viewtype == nil {
+            return MessageKind.text(text)
+        }
+
+        switch self.viewtype! {
+        case .image:
+            return createImageMessage(text: text)
+        case .video:
+            return createVideoMessage(text: text)
+        case .voice, .audio:
+            return createAudioMessage(text: text)
+        default:
+            // TODO: custom views for audio, etc
+            if let filename = self.filename {
+                if Utils.hasAudioSuffix(url: fileURL!) {
+                   return createAudioMessage(text: text)
+                }
+                return createFileMessage(text: text)
+            }
+            return MessageKind.text(text)
+        }
+    }()
+
+    internal func createVideoMessage(text: String) -> MessageKind {
+        let thumbnail = Utils.generateThumbnailFromVideo(url: fileURL)
+        if text.isEmpty {
+            return MessageKind.video(Media(url: fileURL, image: thumbnail))
+        }
+        let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16.0),
+                                                                             NSAttributedString.Key.foregroundColor: DcColors.defaultTextColor])
+        return MessageKind.videoText(Media(url: fileURL, image: thumbnail, text: attributedString))
+    }
+
+    internal func createImageMessage(text: String) -> MessageKind {
+        if text.isEmpty {
+            return MessageKind.photo(Media(image: image))
+        }
+        let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16.0),
+                                                                             NSAttributedString.Key.foregroundColor: DcColors.defaultTextColor])
+        return MessageKind.photoText(Media(image: image, text: attributedString))
+    }
+
+    internal func createAudioMessage(text: String) -> MessageKind {
+        let audioAsset = AVURLAsset(url: fileURL!)
+        let seconds = Float(CMTimeGetSeconds(audioAsset.duration))
+        if !text.isEmpty {
+            let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16.0),
+                                                                                 NSAttributedString.Key.foregroundColor: DcColors.defaultTextColor])
+            return MessageKind.audio(Audio(url: audioAsset.url, duration: seconds, text: attributedString))
+        }
+        return MessageKind.audio(Audio(url: fileURL!, duration: seconds))
+    }
+
+    internal func createFileMessage(text: String) -> MessageKind {
+        let fileString = "\(self.filename ?? "???") (\(self.filesize / 1024) kB)"
+        let attributedFileString = NSMutableAttributedString(string: fileString,
+                                                             attributes: [NSAttributedString.Key.font: UIFont.italicSystemFont(ofSize: 13.0),
+                                                                          NSAttributedString.Key.foregroundColor: DcColors.defaultTextColor])
+        if !text.isEmpty {
+            attributedFileString.append(NSAttributedString(string: "\n\n",
+                                                           attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 7.0)]))
+            attributedFileString.append(NSAttributedString(string: text,
+                                                           attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16.0),
+                                                                        NSAttributedString.Key.foregroundColor: DcColors.defaultTextColor]))
+        }
+        return MessageKind.fileText(Media(text: attributedFileString))
+    }
+
+    var isForwarded: Bool {
+        return dc_msg_is_forwarded(messagePointer) != 0
+    }
+
+    var messageId: String {
+        return "\(id)"
+    }
+
+    var id: Int {
+        return Int(dc_msg_get_id(messagePointer))
+    }
+
+    var fromContactId: Int {
+        return Int(dc_msg_get_from_id(messagePointer))
+    }
+
+    lazy var fromContact: DcContact = {
+        DcContact(id: fromContactId)
+    }()
+
+    var chatId: Int {
+        return Int(dc_msg_get_chat_id(messagePointer))
+    }
+
+    var text: String? {
+        set {
+            if let newValue = newValue {
+                dc_msg_set_text(messagePointer, newValue.cString(using: .utf8))
+            } else {
+                dc_msg_set_text(messagePointer, nil)
+            }
+        }
+        get {
+            guard let cString = dc_msg_get_text(messagePointer) else { return nil }
+            let swiftString = String(cString: cString)
+            dc_str_unref(cString)
+            return swiftString
+        }
+    }
+
+    var viewtype: MessageViewType? {
+        switch dc_msg_get_viewtype(messagePointer) {
+        case 0:
+            return nil
+        case DC_MSG_AUDIO:
+            return .audio
+        case DC_MSG_FILE:
+            return .file
+        case DC_MSG_GIF:
+            return .gif
+        case DC_MSG_TEXT:
+            return .text
+        case DC_MSG_IMAGE:
+            return .image
+        case DC_MSG_STICKER:
+            return .image
+        case DC_MSG_VIDEO:
+            return .video
+        case DC_MSG_VOICE:
+            return .voice
+        default:
+            return nil
+        }
+    }
+
+    var fileURL: URL? {
+        if let file = self.file {
+            return URL(fileURLWithPath: file, isDirectory: false)
+        }
+        return nil
+    }
+
+    private lazy var image: UIImage? = { [unowned self] in
+        let filetype = dc_msg_get_viewtype(messagePointer)
+        if let path = fileURL, filetype == DC_MSG_IMAGE {
+            if path.isFileURL {
+                do {
+                    let data = try Data(contentsOf: path)
+                    let image = UIImage(data: data)
+                    return image
+                } catch {
+                    DcContext.shared.logger?.warning("failed to load image: \(path), \(error)")
+                    return nil
+                }
+            }
+            return nil
+        } else {
+            return nil
+        }
+        }()
+
+    var file: String? {
+        if let cString = dc_msg_get_file(messagePointer) {
+            let str = String(cString: cString)
+            dc_str_unref(cString)
+            return str.isEmpty ? nil : str
+        }
+
+        return nil
+    }
+
+    var filemime: String? {
+        if let cString = dc_msg_get_filemime(messagePointer) {
+            let str = String(cString: cString)
+            dc_str_unref(cString)
+            return str.isEmpty ? nil : str
+        }
+
+        return nil
+    }
+
+    var filename: String? {
+        if let cString = dc_msg_get_filename(messagePointer) {
+            let str = String(cString: cString)
+            dc_str_unref(cString)
+            return str.isEmpty ? nil : str
+        }
+
+        return nil
+    }
+
+    func setFile(filepath: String?, mimeType: String?) {
+        dc_msg_set_file(messagePointer, filepath, mimeType)
+    }
+
+    func setDimension(width: CGFloat, height: CGFloat) {
+        dc_msg_set_dimension(messagePointer, Int32(width), Int32(height))
+    }
+
+    var filesize: Int {
+        return Int(dc_msg_get_filebytes(messagePointer))
+    }
+
+    // DC_MSG_*
+    var type: Int {
+        return Int(dc_msg_get_viewtype(messagePointer))
+    }
+
+    // DC_STATE_*
+    var state: Int {
+        return Int(dc_msg_get_state(messagePointer))
+    }
+
+    var timestamp: Int64 {
+        return Int64(dc_msg_get_timestamp(messagePointer))
+    }
+
+    var isInfo: Bool {
+        return dc_msg_is_info(messagePointer) == 1
+    }
+
+    var isSetupMessage: Bool {
+        return dc_msg_is_setupmessage(messagePointer) == 1
+    }
+
+    var setupCodeBegin: String {
+        guard let cString = dc_msg_get_setupcodebegin(messagePointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    func summary(chars: Int) -> String? {
+        guard let cString = dc_msg_get_summarytext(messagePointer, Int32(chars)) else { return nil }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    func summary(chat: DcChat) -> DcLot {
+        guard let chatPointer = chat.chatPointer else {
+            fatalError()
+        }
+        guard let dcLotPointer = dc_msg_get_summary(messagePointer, chatPointer) else {
+            fatalError()
+        }
+        return DcLot(dcLotPointer)
+    }
+
+    func showPadlock() -> Bool {
+        return dc_msg_get_showpadlock(messagePointer) == 1
+    }
+
+    func sendInChat(id: Int) {
+        dc_send_msg(DcContext.shared.contextPointer, UInt32(id), messagePointer)
+    }
+
+    func previousMediaURLs() -> [URL] {
+        var urls: [URL] = []
+        var prev: Int = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(id), -1, Int32(type), 0, 0))
+        while prev != 0 {
+            let prevMessage = DcMsg(id: prev)
+            if let url = prevMessage.fileURL {
+                urls.insert(url, at: 0)
+            }
+            prev = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(prevMessage.id), -1, Int32(prevMessage.type), 0, 0))
+        }
+        return urls
+    }
+
+    func nextMediaURLs() -> [URL] {
+        var urls: [URL] = []
+        var next: Int = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(id), 1, Int32(type), 0, 0))
+        while next != 0 {
+            let nextMessage = DcMsg(id: next)
+            if let url = nextMessage.fileURL {
+                urls.append(url)
+            }
+            next = Int(dc_get_next_media(DcContext.shared.contextPointer, UInt32(nextMessage.id), 1, Int32(nextMessage.type), 0, 0))
+        }
+        return urls
+    }
+}
+
+class DcContact {
+    private var contactPointer: OpaquePointer?
+
+    init(id: Int) {
+        contactPointer = dc_get_contact(DcContext.shared.contextPointer, UInt32(id))
+    }
+
+    deinit {
+        dc_contact_unref(contactPointer)
+    }
+
+    var displayName: String {
+        guard let cString = dc_contact_get_display_name(contactPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var nameNAddr: String {
+        guard let cString = dc_contact_get_name_n_addr(contactPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var name: String {
+        guard let cString = dc_contact_get_name(contactPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var email: String {
+        guard let cString = dc_contact_get_addr(contactPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var isVerified: Bool {
+        return dc_contact_is_verified(contactPointer) > 0
+    }
+
+    var isBlocked: Bool {
+        return dc_contact_is_blocked(contactPointer) == 1
+    }
+
+    lazy var profileImage: UIImage? = { [unowned self] in
+        guard let cString = dc_contact_get_profile_image(contactPointer) else { return nil }
+        let filename = String(cString: cString)
+        dc_str_unref(cString)
+        let path: URL = URL(fileURLWithPath: filename, isDirectory: false)
+        if path.isFileURL {
+            do {
+                let data = try Data(contentsOf: path)
+                return UIImage(data: data)
+            } catch {
+                DcContext.shared.logger?.warning("failed to load image: \(filename), \(error)")
+                return nil
+            }
+        }
+        return nil
+    }()
+
+    var color: UIColor {
+        return UIColor(netHex: Int(dc_contact_get_color(contactPointer)))
+    }
+
+    var id: Int {
+        return Int(dc_contact_get_id(contactPointer))
+    }
+
+    func block() {
+        dc_block_contact(DcContext.shared.contextPointer, UInt32(id), 1)
+    }
+
+    func unblock() {
+        dc_block_contact(DcContext.shared.contextPointer, UInt32(id), 0)
+    }
+
+    func marknoticed() {
+        dc_marknoticed_contact(DcContext.shared.contextPointer, UInt32(id))
+    }
+}
+
+class DcLot {
+    private var dcLotPointer: OpaquePointer?
+
+    // takes ownership of specified pointer
+    init(_ dcLotPointer: OpaquePointer) {
+        self.dcLotPointer = dcLotPointer
+    }
+
+    deinit {
+        dc_lot_unref(dcLotPointer)
+    }
+
+    var text1: String? {
+        guard let cString = dc_lot_get_text1(dcLotPointer) else { return nil }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var text1Meaning: Int {
+        return Int(dc_lot_get_text1_meaning(dcLotPointer))
+    }
+
+    var text2: String? {
+        guard let cString = dc_lot_get_text2(dcLotPointer) else { return nil }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var timestamp: Int64 {
+        return Int64(dc_lot_get_timestamp(dcLotPointer))
+    }
+
+    var state: Int {
+        return Int(dc_lot_get_state(dcLotPointer))
+    }
+
+    var id: Int {
+        return Int(dc_lot_get_id(dcLotPointer))
+    }
+}
+
+class DcProvider {
+    private var dcProviderPointer: OpaquePointer?
+
+    // takes ownership of specified pointer
+    init(_ dcProviderPointer: OpaquePointer) {
+        self.dcProviderPointer = dcProviderPointer
+    }
+
+    deinit {
+        dc_provider_unref(dcProviderPointer)
+    }
+
+    var status: Int {
+        return Int(dc_provider_get_status(dcProviderPointer))
+    }
+
+    var beforeLoginHint: String {
+        guard let cString = dc_provider_get_before_login_hint(dcProviderPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+
+    var getOverviewPage: String {
+        guard let cString = dc_provider_get_overview_page(dcProviderPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+}
+
+enum ChatType: Int {
+    case SINGLE = 100
+    case GROUP = 120
+    case VERIFIEDGROUP = 130
+}
+
+enum MessageViewType: CustomStringConvertible {
+    case audio
+    case file
+    case gif
+    case image
+    case text
+    case video
+    case voice
+
+    var description: String {
+        switch self {
+        // Use Internationalization, as appropriate.
+        case .audio: return "Audio"
+        case .file: return "File"
+        case .gif: return "GIF"
+        case .image: return "Image"
+        case .text: return "Text"
+        case .video: return "Video"
+        case .voice: return "Voice"
+        }
+    }
+}
+
+func strToBool(_ value: String?) -> Bool {
+    if let vStr = value {
+        if let vInt = Int(vStr) {
+            return vInt == 1
+        }
+        return false
+    }
+
+    return false
+}

+ 191 - 0
DcCore/DcCore/DC/events.swift

@@ -0,0 +1,191 @@
+import UIKit
+import UserNotifications
+
+let dcNotificationChanged = Notification.Name(rawValue: "MrEventMsgsChanged")
+let dcNotificationIncoming = Notification.Name(rawValue: "MrEventIncomingMsg")
+let dcNotificationImexProgress = Notification.Name(rawValue: "dcNotificationImexProgress")
+let dcNotificationConfigureProgress = Notification.Name(rawValue: "MrEventConfigureProgress")
+let dcNotificationSecureJoinerProgress = Notification.Name(rawValue: "MrEventSecureJoinerProgress")
+let dcNotificationSecureInviterProgress = Notification.Name(rawValue: "MrEventSecureInviterProgress")
+let dcNotificationViewChat = Notification.Name(rawValue: "MrEventViewChat")
+let dcNotificationContactChanged = Notification.Name(rawValue: "MrEventContactsChanged")
+let dcNotificationChatModified = Notification.Name(rawValue: "dcNotificationChatModified")
+let dcNotificationChatDeletedInChatDetail = Notification.Name(rawValue: "ChatDeletedInChatDetail")
+
+@_silgen_name("callbackSwift")
+
+public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLong, data1String: UnsafePointer<Int8>, data2String: UnsafePointer<Int8>) {
+    if event >= DC_EVENT_ERROR && event <= 499 {
+        let s = String(cString: data2String)
+        DcContext.shared.lastErrorString = s
+        DcContext.shared.logger?.error("event: \(s)")
+        return
+    }
+
+    switch event {
+
+    case DC_EVENT_INFO:
+        let s = String(cString: data2String)
+        DcContext.shared.logger?.info("event: \(s)")
+
+    case DC_EVENT_WARNING:
+        let s = String(cString: data2String)
+        DcContext.shared.logger?.warning("event: \(s)")
+
+    case DC_EVENT_CONFIGURE_PROGRESS:
+        DcContext.shared.logger?.info("configure progress: \(Int(data1)) \(Int(data2))")
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            let done = Int(data1) == 1000
+
+            nc.post(
+                name: dcNotificationConfigureProgress,
+                object: nil,
+                userInfo: [
+                    "progress": Int(data1),
+                    "error": Int(data1) == 0,
+                    "done": done,
+                    "errorMessage": DcContext.shared.lastErrorString as Any,
+                ]
+            )
+
+            if done {
+                UserDefaults.standard.set(true, forKey: Constants.Keys.deltachatUserProvidedCredentialsKey)
+                UserDefaults.standard.synchronize()
+                DcContext.shared.lastErrorString = nil
+            }
+        }
+
+    case DC_EVENT_IMEX_PROGRESS:
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(
+                name: dcNotificationImexProgress,
+                object: nil,
+                userInfo: [
+                    "progress": Int(data1),
+                    "error": Int(data1) == 0,
+                    "done": Int(data1) == 1000,
+                    "errorMessage": DcContext.shared.lastErrorString as Any,
+                ]
+            )
+        }
+
+    case DC_EVENT_IMAP_CONNECTED, DC_EVENT_SMTP_CONNECTED:
+        DcContext.shared.logger?.warning("network: \(String(cString: data2String))")
+
+    case DC_EVENT_MSGS_CHANGED, DC_EVENT_MSG_READ, DC_EVENT_MSG_DELIVERED, DC_EVENT_MSG_FAILED:
+        DcContext.shared.logger?.info("change: \(event)")
+
+        let nc = NotificationCenter.default
+
+        DispatchQueue.main.async {
+            nc.post(
+                name: dcNotificationChanged,
+                object: nil,
+                userInfo: [
+                    "message_id": Int(data2),
+                    "chat_id": Int(data1),
+                    "date": Date(),
+                ]
+            )
+        }
+
+    case DC_EVENT_CHAT_MODIFIED:
+        DcContext.shared.logger?.info("chat modified: \(event)")
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(
+                name: dcNotificationChatModified,
+                object: nil,
+                userInfo: [
+                    "chat_id": Int(data1),
+                ]
+            )
+        }
+
+    case DC_EVENT_INCOMING_MSG:
+        let nc = NotificationCenter.default
+        let userInfo = [
+            "message_id": Int(data2),
+            "chat_id": Int(data1),
+        ]
+
+        DispatchQueue.main.async {
+            nc.post(name: dcNotificationIncoming,
+                    object: nil,
+                    userInfo: userInfo)
+
+            if !UserDefaults.standard.bool(forKey: "notifications_disabled") {
+                let content = UNMutableNotificationContent()
+                let msg = DcMsg(id: Int(data2))
+                content.title = msg.fromContact.displayName
+                content.body = msg.summary(chars: 40) ?? ""
+                content.userInfo = userInfo
+                content.sound = .default
+
+                let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
+
+                let request = UNNotificationRequest(identifier: Constants.notificationIdentifier, content: content, trigger: trigger)
+                UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
+                DcContext.shared.logger?.info("notifications: added \(content)")
+            }
+
+            let array = DcContext.shared.getFreshMessages()
+            UIApplication.shared.applicationIconBadgeNumber = array.count
+        }
+
+    case DC_EVENT_SMTP_MESSAGE_SENT:
+        DcContext.shared.logger?.info("network: \(String(cString: data2String))")
+
+    case DC_EVENT_MSG_DELIVERED:
+        DcContext.shared.logger?.info("message delivered: \(data1)-\(data2)")
+
+    case DC_EVENT_SECUREJOIN_INVITER_PROGRESS:
+        DcContext.shared.logger?.info("securejoin inviter progress \(data1)")
+
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(
+                name: dcNotificationSecureInviterProgress,
+                object: nil,
+                userInfo: [
+                    "progress": Int(data2),
+                    "error": Int(data2) == 0,
+                    "done": Int(data2) == 1000,
+                ]
+            )
+        }
+
+    case DC_EVENT_SECUREJOIN_JOINER_PROGRESS:
+        DcContext.shared.logger?.info("securejoin joiner progress \(data1)")
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(
+                name: dcNotificationSecureJoinerProgress,
+                object: nil,
+                userInfo: [
+                    "contact_id": Int(data1),
+                    "progress": Int(data2),
+                    "error": Int(data2) == 0,
+                    "done": Int(data2) == 1000,
+                ]
+            )
+        }
+    case DC_EVENT_CONTACTS_CHANGED:
+        DcContext.shared.logger?.info("contact changed: \(data1)")
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(
+                name: dcNotificationContactChanged,
+                object: nil,
+                userInfo: [
+                    "contact_id": Int(data1)
+                ]
+            )
+        }
+
+    default:
+        DcContext.shared.logger?.warning("unknown event: \(event)")
+    }
+}

+ 9 - 0
DcCore/DcCore/DC/wrapper.c

@@ -0,0 +1,9 @@
+#include "wrapper.h"
+
+void callbackSwift(int, long, long, const char*, const char*);
+
+uintptr_t callback_ios(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t data2)
+{
+    callbackSwift(event, data1, data2, data1? (const char*)data1 : "", data2? (const char*)data2 : "");
+    return 0;
+}

+ 18 - 0
DcCore/DcCore/DC/wrapper.h

@@ -0,0 +1,18 @@
+#ifndef wrapper_h
+#define wrapper_h
+
+#include <stdio.h>
+#include "deltachat.h"
+
+// redeclare, so swift understands they are opaque types
+typedef dc_context_t dc_context_t;
+typedef dc_contact_t dc_contact_t;
+typedef dc_chat_t dc_chat_t;
+typedef dc_msg_t dc_msg_t;
+typedef dc_lot_t dc_lot_t;
+typedef dc_array_t dc_array_t;
+typedef dc_chatlist_t dc_chatlist_t;
+
+uintptr_t callback_ios(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t data2);
+
+#endif /* wrapper_h */

+ 21 - 0
DcCore/DcCore/DcCore.h

@@ -0,0 +1,21 @@
+//
+//  DcCore.h
+//  DcCore
+//
+//  Created by Macci on 08.04.20.
+//  Copyright © 2020 merlinux GmbH. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for DcCore.
+FOUNDATION_EXPORT double DcCoreVersionNumber;
+
+//! Project version string for DcCore.
+FOUNDATION_EXPORT const unsigned char DcCoreVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <DcCore/PublicHeader.h>
+
+
+#import <DcCore/wrapper.h>
+#include "deltachat.h"

+ 23 - 0
DcCore/DcCore/Extensions/String+Extensions.swift

@@ -0,0 +1,23 @@
+import Foundation
+internal extension String {
+    
+    static func localized(_ stringID: String) -> String {
+        let value = NSLocalizedString(stringID, comment: "")
+        if value != stringID || NSLocale.preferredLanguages.first == "en" {
+            return value
+        }
+
+        guard
+            let path = Bundle.main.path(forResource: "en", ofType: "lproj"),
+            let bundle = Bundle(path: path)
+        else { return value }
+        return NSLocalizedString(stringID, bundle: bundle, comment: "")
+    }
+
+    static func localized(stringID: String, count: Int) -> String {
+        let formatString: String = localized(stringID)
+        let resultString: String = String.localizedStringWithFormat(formatString, count)
+        return resultString
+    }
+
+}

+ 72 - 0
DcCore/DcCore/Extensions/UIColor+Extensions.swift

@@ -0,0 +1,72 @@
+//
+//  UIColor+Extension.swift
+//  DcCore
+//
+//  Created by Macci on 08.04.20.
+//  Copyright © 2020 merlinux GmbH. All rights reserved.
+//
+
+import UIKit
+
+internal extension UIColor {
+
+    convenience init(alpha: Int, red: Int, green: Int, blue: Int) {
+        assert(red >= 0 && red <= 255, "Invalid red component")
+        assert(green >= 0 && green <= 255, "Invalid green component")
+        assert(blue >= 0 && blue <= 255, "Invalid blue component")
+
+        self.init(red: CGFloat(red) / 255, green: CGFloat(green) / 255, blue: CGFloat(blue) / 255, alpha: CGFloat(alpha) / 255)
+    }
+
+    convenience init(netHex: Int) {
+        var alpha = (netHex >> 24) & 0xFF
+        if alpha == 0 {
+            alpha = 255
+        }
+
+        self.init(alpha: alpha, red: (netHex >> 16) & 0xFF, green: (netHex >> 8) & 0xFF, blue: netHex & 0xFF)
+    }
+
+    // see: https://stackoverflow.com/a/33397427
+    convenience init(hexString: String) {
+        let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
+        var int = UInt32()
+        Scanner(string: hex).scanHexInt32(&int)
+        let a, r, g, b: UInt32
+        switch hex.count {
+        case 3: // RGB (12-bit)
+            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
+        case 6: // RGB (24-bit)
+            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
+        case 8: // ARGB (32-bit)
+            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
+        default:
+            (a, r, g, b) = (255, 0, 0, 0)
+        }
+        self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
+    }
+
+    static func themeColor(light: UIColor, dark: UIColor? = nil) -> UIColor {
+        if let dark = dark {
+            if #available(iOS 13, *) {
+                return UIColor.init { (trait) -> UIColor in
+                    return trait.userInterfaceStyle == .dark ? dark : light
+                }
+            }
+        }
+        return light
+    }
+
+    static func themeColor(lightHex: String, darkHex: String? = nil) -> UIColor {
+        if let darkHex = darkHex {
+            if #available(iOS 13, *) {
+                return UIColor.init { (trait) -> UIColor in
+                    return trait.userInterfaceStyle == .dark ? UIColor(hexString: darkHex) :  UIColor(hexString: lightHex)
+                }
+            }
+        }
+        return UIColor(hexString: lightHex)
+    }
+
+
+}

+ 15 - 0
DcCore/DcCore/Extensions/UIImage+Extension.swift

@@ -0,0 +1,15 @@
+import UIKit
+
+internal extension UIImage {
+    convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
+        let rect = CGRect(origin: .zero, size: size)
+        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
+        color.setFill()
+        UIRectFill(rect)
+        let image = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+
+        guard let cgImage = image?.cgImage else { return nil }
+        self.init(cgImage: cgImage)
+    }
+}

+ 69 - 0
DcCore/DcCore/Helper/Colors.swift

@@ -0,0 +1,69 @@
+import UIKit
+
+struct DcColors {
+    static let primary = UIColor.systemBlue
+    static let colorDisabled = UIColor.themeColor(light: UIColor(white: 0.9, alpha: 1), dark: UIColor(white: 0.2, alpha: 1))
+    static let messagePrimaryColor = UIColor.themeColor(light: UIColor.rgb(red: 220, green: 248, blue: 198),
+                                                        dark: UIColor.init(hexString: "224508"))
+    static let messageSecondaryColor = UIColor.themeColor(light: UIColor.init(hexString: "ebebed"),
+                                                          dark: UIColor.init(hexString: "333333"))
+    static let contactCellBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
+    static let defaultBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
+    static let chatBackgroundColor = UIColor.themeColor(light: UIColor(red: 255, green: 255, blue: 255, alpha: 0), dark: .black)
+    static let checkmarkGreen = UIColor.themeColor(light: UIColor.rgb(red: 112, green: 177, blue: 92))
+    static let defaultTextColor = UIColor.themeColor(light: .darkText, dark: .white)
+    static let grayTextColor = UIColor.themeColor(light: .darkGray, dark: .lightGray)
+    static let grayDateColor = UIColor.themeColor(lightHex: "999999", darkHex: "bbbbbb") // slight variations of lightGray (#aaaaaa)
+    static let middleGray = UIColor(hexString: "848ba7")
+    static let secondaryTextColor = UIColor.themeColor(lightHex: "848ba7", darkHex: "a5abc0")
+    static let inputFieldColor =  UIColor.themeColor(light: UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1),
+                                                     dark: UIColor(red: 10 / 255, green: 10 / 255, blue: 10 / 255, alpha: 1))
+    static let placeholderColor = UIColor.themeColor(light: UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1),
+                                                     dark: UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1))
+    static let providerPreparationBackground = UIColor.themeColor(lightHex: "#fffdf7b2", darkHex: "##fffdf7b2")
+    static let providerBrokenBackground = UIColor.themeColor(light: SystemColor.red.uiColor, dark: SystemColor.red.uiColor)
+    static let systemMessageBackgroundColor = UIColor.themeColor(light: UIColor.rgb(red: 248, green: 248, blue: 248), dark: UIColor(white: 0.2, alpha: 0.5))
+    static let deaddropBackground = UIColor.themeColor(light: UIColor.init(hexString: "ebebec"), dark: UIColor.init(hexString: "1a1a1c"))
+}
+
+enum SystemColor {
+    case red
+    case orange
+    case yellow
+    case green
+    case tealBlue
+    case blue
+    case purple
+    case pink
+
+    var uiColor: UIColor {
+        switch self {
+        case .red:
+            return UIColor(red: 255 / 255, green: 59 / 255, blue: 48 / 255, alpha: 1)
+        case .orange:
+            return UIColor(red: 255 / 255, green: 149 / 255, blue: 0 / 255, alpha: 1)
+        case .yellow:
+            return UIColor(red: 255 / 255, green: 204 / 255, blue: 0 / 255, alpha: 1)
+        case .green:
+            return UIColor(red: 76 / 255, green: 217 / 255, blue: 100 / 255, alpha: 1)
+        case .tealBlue:
+            return UIColor(red: 90 / 255, green: 200 / 255, blue: 250 / 255, alpha: 1)
+        case .blue:
+            return UIColor(red: 0 / 255, green: 122 / 255, blue: 255 / 255, alpha: 1)
+        case .purple:
+            return UIColor(red: 88 / 255, green: 86 / 255, blue: 214 / 255, alpha: 1)
+        case .pink:
+            return UIColor(red: 255 / 255, green: 45 / 255, blue: 85 / 255, alpha: 1)
+        }
+    }
+}
+
+extension UIColor {
+    static func rgb(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
+        return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1)
+    }
+
+    static var systemBlue: UIColor {
+        return UIButton(type: .system).tintColor
+    }
+}

+ 30 - 0
DcCore/DcCore/Helper/Constants.swift

@@ -0,0 +1,30 @@
+import UIKit
+
+struct Constants {
+    struct Color {
+        static let bubble = UIColor(netHex: 0xEFFFDE)
+    }
+
+    struct Keys {
+        static let deltachatUserProvidedCredentialsKey = "__DELTACHAT_USER_PROVIDED_CREDENTIALS_KEY__"
+        static let deltachatImapEmailKey = "__DELTACHAT_IMAP_EMAIL_KEY__"
+        static let deltachatImapPasswordKey = "__DELTACHAT_IMAP_PASSWORD_KEY__"
+    }
+
+    static let defaultShadow = UIImage(color: UIColor(hexString: "ff2b82"), size: CGSize(width: 1, height: 1))
+    static let onlineShadow = UIImage(color: UIColor(hexString: "3ed67e"), size: CGSize(width: 1, height: 1))
+
+    static let notificationIdentifier = "deltachat-ios-local-notifications"
+
+    static let defaultCellHeight: CGFloat = 48
+    static let defaultHeaderHeight: CGFloat = 20
+}
+
+struct Time {
+    static let twoMinutes = 2 * 60
+    static let fiveMinutes = 5 * 60
+    static let thirtyMinutes = 30 * 6
+    static let oneHour = 60 * 60
+    static let twoHours = 2 * 60 * 60
+    static let sixHours = 6 * 60 * 60
+}

+ 217 - 0
DcCore/DcCore/Helper/Utils.swift

@@ -0,0 +1,217 @@
+import Foundation
+import UIKit
+import AVFoundation
+
+struct Utils {
+
+    static func getInitials(inputName: String) -> String {
+        if let firstLetter = inputName.first {
+            return firstLetter.uppercased()
+        } else {
+            return ""
+        }
+    }
+
+    static func copyAndFreeArray(inputArray: OpaquePointer?) -> [Int] {
+        var acc: [Int] = []
+        let len = dc_array_get_cnt(inputArray)
+        for i in 0 ..< len {
+            let e = dc_array_get_id(inputArray, i)
+            acc.append(Int(e))
+        }
+        dc_array_unref(inputArray)
+
+        return acc
+    }
+
+    static func copyAndFreeArrayWithLen(inputArray: OpaquePointer?, len: Int = 0) -> [Int] {
+        var acc: [Int] = []
+        let arrayLen = dc_array_get_cnt(inputArray)
+        let start = max(0, arrayLen - len)
+        for i in start ..< arrayLen {
+            let e = dc_array_get_id(inputArray, i)
+            acc.append(Int(e))
+        }
+        dc_array_unref(inputArray)
+
+        return acc
+    }
+
+    static func copyAndFreeArrayWithOffset(inputArray: OpaquePointer?, len: Int = 0, from: Int = 0, skipEnd: Int = 0) -> [Int] {
+        let lenArray = dc_array_get_cnt(inputArray)
+        if lenArray <= skipEnd || lenArray == 0 {
+            dc_array_unref(inputArray)
+            return []
+        }
+
+        let start = lenArray - 1 - skipEnd
+        let end = max(0, start - len)
+        let finalLen = start - end + (len > 0 ? 0 : 1)
+        var acc: [Int] = [Int](repeating: 0, count: finalLen)
+
+        for i in stride(from: start, to: end, by: -1) {
+            let index = finalLen - (start - i) - 1
+            acc[index] = Int(dc_array_get_id(inputArray, i))
+        }
+
+        dc_array_unref(inputArray)
+        DcContext.shared.logger?.info("got: \(from) \(len) \(lenArray) - \(acc)")
+
+        return acc
+    }
+
+/*    static func formatAddressForQuery(address: [String: String]) -> String {
+        // Open address in Apple Maps app.
+        var addressParts = [String]()
+        let addAddressPart: ((String?) -> Void) = { part in
+            guard let part = part else {
+                return
+            }
+            guard !part.isEmpty else {
+                return
+            }
+            addressParts.append(part)
+        }
+        addAddressPart(address["Street"])
+        addAddressPart(address["Neighborhood"])
+        addAddressPart(address["City"])
+        addAddressPart(address["Region"])
+        addAddressPart(address["Postcode"])
+        addAddressPart(address["Country"])
+        return addressParts.joined(separator: ", ")
+    }
+
+    // compression needs to be done before in UIImage.dcCompress()
+    static func saveImage(image: UIImage) -> String? {
+        guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask,
+            appropriateFor: nil, create: false) as NSURL else {
+            return nil
+        }
+
+        guard let data = image.isTransparent() ? image.pngData() : image.jpegData(compressionQuality: 1.0) else {
+            return nil
+        }
+
+        do {
+            let timestamp = Double(Date().timeIntervalSince1970)
+            let path = directory.appendingPathComponent("\(timestamp).jpg")
+            try data.write(to: path!)
+            return path?.relativePath
+        } catch {
+            logger.info(error.localizedDescription)
+            return nil
+        }
+    }
+     */
+
+    static func hasAudioSuffix(url: URL) -> Bool {
+        ///TODO: add more file suffixes
+        return url.absoluteString.hasSuffix("wav")
+    }
+
+
+
+    static func generateThumbnailFromVideo(url: URL?) -> UIImage? {
+        guard let url = url else {
+            return nil
+        }
+        do {
+            let asset = AVURLAsset(url: url)
+            let imageGenerator = AVAssetImageGenerator(asset: asset)
+            imageGenerator.appliesPreferredTrackTransform = true
+            let cgImage = try imageGenerator.copyCGImage(at: .zero, actualTime: nil)
+            return UIImage(cgImage: cgImage)
+        } catch {
+            print(error.localizedDescription)
+            return nil
+        }
+    }
+
+    /*
+    static func getDeviceLanguage() -> String? {
+        // some device languages have suffixes (like en-aus etc.) so we want to cut suffixes off
+        guard let lang = Locale.preferredLanguages.first?.split(separator: "-").first else {
+            return nil
+        }
+        return String(lang)
+    }
+     */
+}
+
+class DateUtils {
+    typealias DtU = DateUtils
+    static let minute: Double = 60
+    static let hour: Double = 3600
+    static let day: Double = 86400
+    static let year: Double = 365 * day
+
+    static func getRelativeTimeInSeconds(timeStamp: Double) -> Double {
+        let unixTime = Double(Date().timeIntervalSince1970)
+        return unixTime - timeStamp
+    }
+
+    private static func is24hDefault() -> Bool {
+        let dateString: String = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: Locale.current) ?? ""
+        return !dateString.contains("a")
+    }
+
+    private static func getLocalDateFormatter() -> DateFormatter {
+        let formatter = DateFormatter()
+        formatter.timeZone = .current
+        formatter.locale = .current
+        return formatter
+    }
+
+    static func getExtendedRelativeTimeSpanString(timeStamp: Double) -> String {
+        let seconds = getRelativeTimeInSeconds(timeStamp: timeStamp)
+        let date = Date(timeIntervalSince1970: timeStamp)
+        let formatter = getLocalDateFormatter()
+        let is24h = is24hDefault()
+
+        if seconds < DtU.minute {
+            return String.localized("now")
+        } else if seconds < DtU.hour {
+            let mins = seconds / DtU.minute
+            return String.localized(stringID: "n_minutes", count: Int(mins))
+        } else if seconds < DtU.day {
+            formatter.dateFormat = is24h ?  "HH:mm" : "hh:mm a"
+            return formatter.string(from: date)
+        } else if seconds < 6 * DtU.day {
+            formatter.dateFormat = is24h ?  "EEE, HH:mm" : "EEE, hh:mm a"
+            return formatter.string(from: date)
+        } else if seconds < DtU.year {
+            formatter.dateFormat = is24h ? "MMM d, HH:mm" : "MMM d, hh:mm a"
+            return formatter.string(from: date)
+        } else {
+            formatter.dateFormat = is24h ? "MMM d, yyyy, HH:mm" : "MMM d, yyyy, hh:mm a"
+            return formatter.string(from: date)
+        }
+    }
+
+    static func getBriefRelativeTimeSpanString(timeStamp: Double) -> String {
+        let seconds = getRelativeTimeInSeconds(timeStamp: timeStamp)
+        let date = Date(timeIntervalSince1970: timeStamp)
+        let formatter = getLocalDateFormatter()
+
+        if seconds < DtU.minute {
+            return String.localized("now")	// under one minute
+        } else if seconds < DtU.hour {
+            let mins = seconds / DtU.minute
+            return String.localized(stringID: "n_minutes", count: Int(mins))
+        } else if seconds < DtU.day {
+            let hours = seconds / DtU.hour
+            return String.localized(stringID: "n_hours", count: Int(hours))
+        } else if seconds < DtU.day * 6 {
+            formatter.dateFormat = "EEE"
+            return formatter.string(from: date)
+        } else if seconds < DtU.year {
+            formatter.dateFormat = "MMM d"
+            return formatter.string(from: date)
+        } else {
+            formatter.dateFormat = "MMM d, yyyy"
+            let localDate = formatter.string(from: date)
+            return localDate
+        }
+    }
+
+}

+ 22 - 0
DcCore/DcCore/Info.plist

@@ -0,0 +1,22 @@
+<?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>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+</dict>
+</plist>

+ 83 - 0
DcCore/DcCore/MessageKit/MessageKind.swift

@@ -0,0 +1,83 @@
+/*
+ MIT License
+ 
+ Copyright (c) 2017-2019 MessageKit
+ 
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import Foundation
+
+/// An enum representing the kind of message and its underlying kind.
+public enum MessageKind {
+
+    /// A standard text message.
+    ///
+    /// - Note: The font used for this message will be the value of the
+    /// `messageLabelFont` property in the `MessagesCollectionViewFlowLayout` object.
+    ///
+    /// Using `MessageKind.attributedText(NSAttributedString)` doesn't require you
+    /// to set this property and results in higher performance.
+    case text(String)
+
+    /// A message with attributed text.
+    case attributedText(NSAttributedString)
+
+    case info(NSAttributedString)
+
+    /// A photo message.
+    case photo(MediaItem)
+
+    /// A photo message with a textual description
+    case photoText(MediaItem)
+
+    /// A file message with an optional text
+    case fileText(MediaItem)
+
+    /// A video message.
+    case video(MediaItem)
+
+    /// A video message with a textual description
+    case videoText(MediaItem)
+
+    /// A location message.
+    case location(LocationItem)
+
+    /// An emoji message.
+    case emoji(String)
+
+    /// An audio message.
+    case audio(AudioItem)
+    
+    /// A contact message.
+    case contact(ContactItem)
+
+    /// A custom message.
+    /// - Note: Using this case requires that you implement the following methods and handle this case:
+    ///   - MessagesDataSource: customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
+    ///   - MessagesLayoutDelegate: customCellSizeCalculator(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator
+    case custom(Any?)
+
+    // MARK: - Not supported yet
+
+//    case system(String)
+//    
+//    case placeholder
+
+}

+ 42 - 0
DcCore/DcCore/MessageKit/Protocols/AudioItem.swift

@@ -0,0 +1,42 @@
+/*
+ MIT License
+
+ Copyright (c) 2017-2019 MessageKit
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import class AVFoundation.AVAudioPlayer
+import UIKit
+
+/// A protocol used to represent the data for an audio message.
+public protocol AudioItem {
+
+    /// The url where the audio file is located.
+    var url: URL { get }
+
+    /// The audio file duration in seconds.
+    var duration: Float { get }
+
+    /// The size of the audio item.
+    var size: CGSize { get }
+
+    /// Additional text
+    var text: NSAttributedString? { get }
+}

+ 41 - 0
DcCore/DcCore/MessageKit/Protocols/ContactItem.swift

@@ -0,0 +1,41 @@
+/*
+ MIT License
+ 
+ Copyright (c) 2017-2018 MessageKit
+ 
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import Foundation
+
+/// A protocol used to represent the data for a contact message.
+public protocol ContactItem {
+    
+    /// contact displayed name
+    var displayName: String { get }
+    
+    /// initials from contact first and last name
+    var initials: String { get }
+    
+    /// contact phone numbers
+    var phoneNumbers: [String] { get }
+    
+    /// contact emails
+    var emails: [String] { get }
+}

+ 37 - 0
DcCore/DcCore/MessageKit/Protocols/LocationItem.swift

@@ -0,0 +1,37 @@
+/*
+ MIT License
+
+ Copyright (c) 2017-2019 MessageKit
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import class CoreLocation.CLLocation
+import UIKit
+
+/// A protocol used to represent the data for a location message.
+public protocol LocationItem {
+
+    /// The location.
+    var location: CLLocation { get }
+
+    /// The size of the location item.
+    var size: CGSize { get }
+
+}

+ 45 - 0
DcCore/DcCore/MessageKit/Protocols/MediaItem.swift

@@ -0,0 +1,45 @@
+/*
+ MIT License
+
+ Copyright (c) 2017-2019 MessageKit
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import Foundation
+import UIKit
+
+/// A protocol used to represent the data for a media message.
+public protocol MediaItem {
+
+    /// The url where the media is located.
+    var url: URL? { get }
+
+    /// The image.
+    var image: UIImage? { get }
+
+    /// A placeholder image for when the image is obtained asychronously.
+    var placeholderImage: UIImage { get }
+
+    /// The size of the media item.
+    var size: CGSize { get }
+
+    var text: NSAttributedString? { get }
+
+}

+ 43 - 0
DcCore/DcCore/MessageKit/Protocols/MessageType.swift

@@ -0,0 +1,43 @@
+/*
+ MIT License
+ 
+ Copyright (c) 2017-2019 MessageKit
+ 
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import Foundation
+
+/// A standard protocol representing a message.
+/// Use this protocol to create your own message object to be used by MessageKit.
+public protocol MessageType {
+
+    /// The sender of the message.
+    var sender: SenderType { get }
+
+    /// The unique identifier for the message.
+    var messageId: String { get }
+
+    /// The date the message was sent.
+    var sentDate: Date { get }
+
+    /// The kind of message and its underlying kind.
+    var kind: MessageKind { get }
+
+}

+ 38 - 0
DcCore/DcCore/MessageKit/Protocols/SenderType.swift

@@ -0,0 +1,38 @@
+/*
+ MIT License
+
+ Copyright (c) 2017-2019 MessageKit
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import Foundation
+
+/// A standard protocol representing a sender.
+/// Use this protocol to adhere a object as the sender of a MessageType
+public protocol SenderType {
+
+    /// The unique String identifier for the sender.
+    ///
+    /// Note: This value must be unique across all senders.
+    var senderId: String { get }
+
+    /// The display name of a sender.
+    var displayName: String { get }
+}

+ 57 - 0
DcCore/DcCore/MessageKit/Sender.swift

@@ -0,0 +1,57 @@
+/*
+ MIT License
+ 
+ Copyright (c) 2017-2019 MessageKit
+ 
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import Foundation
+
+/// An object that groups the metadata of a messages sender.
+@available(*, deprecated: 3.0.0, message: "`Sender` has been replaced with the `SenderType` protocol in 3.0.0")
+public struct Sender: SenderType {
+
+    /// MARK: - Properties
+
+    /// The unique String identifier for the sender.
+    ///
+    /// Note: This value must be unique across all senders.
+    public let senderId: String
+
+    @available(*, deprecated: 3.0.0, message: "`id` has been renamed `senderId` as defined in the `SenderType` protocol")
+    public var id: String {
+        return senderId
+    }
+
+    /// The display name of a sender.
+    public let displayName: String
+
+    // MARK: - Intializers
+
+    public init(senderId: String, displayName: String) {
+        self.senderId = senderId
+        self.displayName = displayName
+    }
+
+    @available(*, deprecated: 3.0.0, message: "`id` has been renamed `senderId` as defined in the `SenderType` protocol")
+    public init(id: String, displayName: String) {
+        self.init(senderId: id, displayName: displayName)
+    }
+}

+ 19 - 0
DcCore/DcCore/Model/Audio.swift

@@ -0,0 +1,19 @@
+import CoreLocation
+import Foundation
+import UIKit
+
+struct Audio: AudioItem {
+    var size: CGSize = CGSize(width: 250, height: 50)
+
+    var url: URL
+
+    var duration: Float
+
+    var text: NSAttributedString?
+
+    init(url: URL, duration: Float, text: NSAttributedString? = nil) {
+        self.url = url
+        self.duration = duration
+        self.text = text
+    }
+}

+ 14 - 0
DcCore/DcCore/Model/Location.swift

@@ -0,0 +1,14 @@
+import CoreLocation
+import Foundation
+import UIKit
+
+struct Location: LocationItem {
+    var location: CLLocation
+
+    var size: CGSize
+
+    init(location: CLLocation, size: CGSize) {
+        self.location = location
+        self.size = size
+    }
+}

+ 26 - 0
DcCore/DcCore/Model/Media.swift

@@ -0,0 +1,26 @@
+import CoreLocation
+import Foundation
+import UIKit
+
+struct Media: MediaItem {
+    var url: URL?
+
+    var image: UIImage?
+
+    var placeholderImage: UIImage = UIImage(color: .gray, size: CGSize(width: 250, height: 100))!
+    var text: NSAttributedString?
+
+    var size: CGSize {
+        if let image = image {
+            return image.size
+        } else {
+            return placeholderImage.size
+        }
+    }
+
+    init(url: URL? = nil, image: UIImage? = nil, text: NSAttributedString? = nil) {
+        self.url = url
+        self.image = image
+        self.text = text
+    }
+}

+ 45 - 0
DcCore/DcCore/Model/Message.swift

@@ -0,0 +1,45 @@
+import CoreLocation
+import Foundation
+import UIKit
+
+struct Message: MessageType {
+    var messageId: String
+    var sender: SenderType
+    var sentDate: Date
+    var kind: MessageKind
+
+    init(kind: MessageKind, sender: Sender, messageId: String, date: Date) {
+        self.kind = kind
+        self.sender = sender
+        self.messageId = messageId
+        sentDate = date
+    }
+
+    init(text: String, sender: Sender, messageId: String, date: Date) {
+        self.init(kind: .text(text), sender: sender, messageId: messageId, date: date)
+    }
+
+    init(attributedText: NSAttributedString, sender: Sender, messageId: String, date: Date) {
+        self.init(kind: .attributedText(attributedText), sender: sender, messageId: messageId, date: date)
+    }
+
+    init(image: UIImage, sender: Sender, messageId: String, date: Date) {
+        let media = Media(image: image)
+        self.init(kind: .photo(media), sender: sender, messageId: messageId, date: date)
+    }
+
+    init(thumbnail: UIImage, sender: Sender, messageId: String, date: Date) {
+        let url = URL(fileURLWithPath: "")
+        let media = Media(url: url, image: thumbnail)
+        self.init(kind: .video(media), sender: sender, messageId: messageId, date: date)
+    }
+
+    init(location: CLLocation, sender: Sender, messageId: String, date: Date) {
+        let locationItem = Location(location: location, size: CGSize(width: 100, height: 50))
+        self.init(kind: .location(locationItem), sender: sender, messageId: messageId, date: date)
+    }
+
+    init(emoji: String, sender: Sender, messageId: String, date: Date) {
+        self.init(kind: .emoji(emoji), sender: sender, messageId: messageId, date: date)
+    }
+}

+ 34 - 0
DcCore/DcCoreTests/DcCoreTests.swift

@@ -0,0 +1,34 @@
+//
+//  DcCoreTests.swift
+//  DcCoreTests
+//
+//  Created by Macci on 08.04.20.
+//  Copyright © 2020 merlinux GmbH. All rights reserved.
+//
+
+import XCTest
+@testable import DcCore
+
+class DcCoreTests: XCTestCase {
+
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+    func testExample() throws {
+        // This is an example of a functional test case.
+        // Use XCTAssert and related functions to verify your tests produce the correct results.
+    }
+
+    func testPerformanceExample() throws {
+        // This is an example of a performance test case.
+        self.measure {
+            // Put the code you want to measure the time of here.
+        }
+    }
+
+}

+ 22 - 0
DcCore/DcCoreTests/Info.plist

@@ -0,0 +1,22 @@
+<?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>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>