diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a7d447 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Package.resolved +.build/* \ No newline at end of file diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..d2812f0 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +git "https://github.com/SwiftyJSON/SwiftyJSON.git" diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..095993d --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "SwiftyJSON/SwiftyJSON" "5.0.0" diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..d856fef --- /dev/null +++ b/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "QiscusCoreAPI", + platforms: [ + .iOS(.v10) + ], + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "QiscusCoreAPI", + targets: ["QiscusCoreAPI"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package(url: "https://github.com/SwiftyJSON/SwiftyJSON", from: "5.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "QiscusCoreAPI", + dependencies: ["SwiftyJSON"]) + ] +) diff --git a/QiscusCoreAPI.podspec b/QiscusCoreAPI.podspec index 8b156f0..a1cdff2 100644 --- a/QiscusCoreAPI.podspec +++ b/QiscusCoreAPI.podspec @@ -8,7 +8,6 @@ s.license = "MIT" s.author = "Qiscus" s.source = { :git => "https://github.com/Qiscus-Integration/QiscusCoreApi.git", :tag => "#{s.version}" } s.platform = :ios, "10.0" -s.ios.vendored_frameworks = 'QiscusCoreAPI.framework' s.ios.frameworks = ["UIKit", "QuartzCore", "CFNetwork", "Security", "Foundation", "MobileCoreServices", "CoreData"] s.dependency 'SwiftyJSON' diff --git a/QiscusCoreAPI.xcodeproj/project.pbxproj b/QiscusCoreAPI.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3f853ff --- /dev/null +++ b/QiscusCoreAPI.xcodeproj/project.pbxproj @@ -0,0 +1,709 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 11449A6F249DD16C00E32D9F /* QiscusCoreAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11449A65249DD16B00E32D9F /* QiscusCoreAPI.framework */; }; + 11449A74249DD16C00E32D9F /* QiscusCoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A73249DD16C00E32D9F /* QiscusCoreAPITests.swift */; }; + 11449A76249DD16C00E32D9F /* QiscusCoreAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 11449A68249DD16B00E32D9F /* QiscusCoreAPI.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 11449AAE249DD1B900E32D9F /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A81249DD1B900E32D9F /* ConfigManager.swift */; }; + 11449AAF249DD1B900E32D9F /* QiscusCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A82249DD1B900E32D9F /* QiscusCore.swift */; }; + 11449AB0249DD1B900E32D9F /* NetworkMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A84249DD1B900E32D9F /* NetworkMessage.swift */; }; + 11449AB1249DD1B900E32D9F /* NetworkRoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A85249DD1B900E32D9F /* NetworkRoom.swift */; }; + 11449AB2249DD1B900E32D9F /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A86249DD1B900E32D9F /* NetworkManager.swift */; }; + 11449AB3249DD1B900E32D9F /* ParameterEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A88249DD1B900E32D9F /* ParameterEncoder.swift */; }; + 11449AB4249DD1B900E32D9F /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A89249DD1B900E32D9F /* ParameterEncoding.swift */; }; + 11449AB5249DD1B900E32D9F /* HTTPUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A8B249DD1B900E32D9F /* HTTPUtil.swift */; }; + 11449AB6249DD1B900E32D9F /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A8C249DD1B900E32D9F /* Router.swift */; }; + 11449AB7249DD1B900E32D9F /* EndpointComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A8E249DD1B900E32D9F /* EndpointComment.swift */; }; + 11449AB8249DD1B900E32D9F /* EndpointRoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A8F249DD1B900E32D9F /* EndpointRoom.swift */; }; + 11449AB9249DD1B900E32D9F /* EndpointUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A90249DD1B900E32D9F /* EndpointUser.swift */; }; + 11449ABA249DD1B900E32D9F /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A91249DD1B900E32D9F /* Endpoint.swift */; }; + 11449ABB249DD1B900E32D9F /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A93249DD1B900E32D9F /* Interface.swift */; }; + 11449ABC249DD1B900E32D9F /* QiscusConnectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A94249DD1B900E32D9F /* QiscusConnectionDelegate.swift */; }; + 11449ABD249DD1B900E32D9F /* QiscusCoreAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 11449A95249DD1B900E32D9F /* QiscusCoreAPI.h */; }; + 11449ABE249DD1B900E32D9F /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A97249DD1B900E32D9F /* Data.swift */; }; + 11449ABF249DD1B900E32D9F /* UIDeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A98249DD1B900E32D9F /* UIDeviceInfo.swift */; }; + 11449AC0249DD1B900E32D9F /* CommentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A9A249DD1B900E32D9F /* CommentModel.swift */; }; + 11449AC1249DD1B900E32D9F /* FileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A9B249DD1B900E32D9F /* FileModel.swift */; }; + 11449AC2249DD1B900E32D9F /* QiscusConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A9D249DD1B900E32D9F /* QiscusConfig.swift */; }; + 11449AC3249DD1B900E32D9F /* NonceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A9E249DD1B900E32D9F /* NonceModel.swift */; }; + 11449AC4249DD1B900E32D9F /* LBModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449A9F249DD1B900E32D9F /* LBModel.swift */; }; + 11449AC5249DD1B900E32D9F /* UserModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA0249DD1B900E32D9F /* UserModel.swift */; }; + 11449AC6249DD1B900E32D9F /* QError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA1249DD1B900E32D9F /* QError.swift */; }; + 11449AC7249DD1B900E32D9F /* RoomEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA2249DD1B900E32D9F /* RoomEvent.swift */; }; + 11449AC8249DD1B900E32D9F /* CommentInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA3249DD1B900E32D9F /* CommentInfo.swift */; }; + 11449AC9249DD1B900E32D9F /* RoomModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA4249DD1B900E32D9F /* RoomModel.swift */; }; + 11449ACA249DD1B900E32D9F /* GenericModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA5249DD1B900E32D9F /* GenericModel.swift */; }; + 11449ACB249DD1B900E32D9F /* ApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AA7249DD1B900E32D9F /* ApiResponse.swift */; }; + 11449ACC249DD1B900E32D9F /* QiscusLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11449AAC249DD1B900E32D9F /* QiscusLogger.swift */; }; + 11449AD0249DD1D500E32D9F /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11449ACF249DD1D500E32D9F /* SwiftyJSON.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 11449A70249DD16C00E32D9F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 11449A5C249DD16B00E32D9F /* Project object */; + proxyType = 1; + remoteGlobalIDString = 11449A64249DD16B00E32D9F; + remoteInfo = QiscusCoreAPI; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 11449A65249DD16B00E32D9F /* QiscusCoreAPI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QiscusCoreAPI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 11449A68249DD16B00E32D9F /* QiscusCoreAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QiscusCoreAPI.h; sourceTree = ""; }; + 11449A69249DD16B00E32D9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 11449A6E249DD16C00E32D9F /* QiscusCoreAPITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QiscusCoreAPITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 11449A73249DD16C00E32D9F /* QiscusCoreAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QiscusCoreAPITests.swift; sourceTree = ""; }; + 11449A75249DD16C00E32D9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 11449A81249DD1B900E32D9F /* ConfigManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = ""; }; + 11449A82249DD1B900E32D9F /* QiscusCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiscusCore.swift; sourceTree = ""; }; + 11449A84249DD1B900E32D9F /* NetworkMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMessage.swift; sourceTree = ""; }; + 11449A85249DD1B900E32D9F /* NetworkRoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRoom.swift; sourceTree = ""; }; + 11449A86249DD1B900E32D9F /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; + 11449A88249DD1B900E32D9F /* ParameterEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParameterEncoder.swift; sourceTree = ""; }; + 11449A89249DD1B900E32D9F /* ParameterEncoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParameterEncoding.swift; sourceTree = ""; }; + 11449A8B249DD1B900E32D9F /* HTTPUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUtil.swift; sourceTree = ""; }; + 11449A8C249DD1B900E32D9F /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; + 11449A8E249DD1B900E32D9F /* EndpointComment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointComment.swift; sourceTree = ""; }; + 11449A8F249DD1B900E32D9F /* EndpointRoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointRoom.swift; sourceTree = ""; }; + 11449A90249DD1B900E32D9F /* EndpointUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointUser.swift; sourceTree = ""; }; + 11449A91249DD1B900E32D9F /* Endpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; + 11449A93249DD1B900E32D9F /* Interface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interface.swift; sourceTree = ""; }; + 11449A94249DD1B900E32D9F /* QiscusConnectionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiscusConnectionDelegate.swift; sourceTree = ""; }; + 11449A95249DD1B900E32D9F /* QiscusCoreAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QiscusCoreAPI.h; sourceTree = ""; }; + 11449A97249DD1B900E32D9F /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; + 11449A98249DD1B900E32D9F /* UIDeviceInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDeviceInfo.swift; sourceTree = ""; }; + 11449A9A249DD1B900E32D9F /* CommentModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentModel.swift; sourceTree = ""; }; + 11449A9B249DD1B900E32D9F /* FileModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileModel.swift; sourceTree = ""; }; + 11449A9D249DD1B900E32D9F /* QiscusConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiscusConfig.swift; sourceTree = ""; }; + 11449A9E249DD1B900E32D9F /* NonceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonceModel.swift; sourceTree = ""; }; + 11449A9F249DD1B900E32D9F /* LBModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBModel.swift; sourceTree = ""; }; + 11449AA0249DD1B900E32D9F /* UserModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserModel.swift; sourceTree = ""; }; + 11449AA1249DD1B900E32D9F /* QError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QError.swift; sourceTree = ""; }; + 11449AA2249DD1B900E32D9F /* RoomEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomEvent.swift; sourceTree = ""; }; + 11449AA3249DD1B900E32D9F /* CommentInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentInfo.swift; sourceTree = ""; }; + 11449AA4249DD1B900E32D9F /* RoomModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomModel.swift; sourceTree = ""; }; + 11449AA5249DD1B900E32D9F /* GenericModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenericModel.swift; sourceTree = ""; }; + 11449AA7249DD1B900E32D9F /* ApiResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiResponse.swift; sourceTree = ""; }; + 11449AA8249DD1B900E32D9F /* QiscusCoreAPI.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = QiscusCoreAPI.modulemap; sourceTree = ""; }; + 11449AAA249DD1B900E32D9F /* QiscusCoreAPI.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = QiscusCoreAPI.modulemap; sourceTree = ""; }; + 11449AAC249DD1B900E32D9F /* QiscusLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiscusLogger.swift; sourceTree = ""; }; + 11449ACF249DD1D500E32D9F /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/iOS/SwiftyJSON.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 11449A62249DD16B00E32D9F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 11449AD0249DD1D500E32D9F /* SwiftyJSON.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 11449A6B249DD16C00E32D9F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 11449A6F249DD16C00E32D9F /* QiscusCoreAPI.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 11449A5B249DD16B00E32D9F = { + isa = PBXGroup; + children = ( + 11449A67249DD16B00E32D9F /* QiscusCoreAPI */, + 11449A72249DD16C00E32D9F /* QiscusCoreAPITests */, + 11449A66249DD16B00E32D9F /* Products */, + 11449ACE249DD1D500E32D9F /* Frameworks */, + ); + sourceTree = ""; + }; + 11449A66249DD16B00E32D9F /* Products */ = { + isa = PBXGroup; + children = ( + 11449A65249DD16B00E32D9F /* QiscusCoreAPI.framework */, + 11449A6E249DD16C00E32D9F /* QiscusCoreAPITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 11449A67249DD16B00E32D9F /* QiscusCoreAPI */ = { + isa = PBXGroup; + children = ( + 11449A7F249DD1B900E32D9F /* Source */, + 11449A68249DD16B00E32D9F /* QiscusCoreAPI.h */, + 11449A69249DD16B00E32D9F /* Info.plist */, + ); + path = QiscusCoreAPI; + sourceTree = ""; + }; + 11449A72249DD16C00E32D9F /* QiscusCoreAPITests */ = { + isa = PBXGroup; + children = ( + 11449A73249DD16C00E32D9F /* QiscusCoreAPITests.swift */, + 11449A75249DD16C00E32D9F /* Info.plist */, + ); + path = QiscusCoreAPITests; + sourceTree = ""; + }; + 11449A7F249DD1B900E32D9F /* Source */ = { + isa = PBXGroup; + children = ( + 11449A80249DD1B900E32D9F /* QiscusCoreApi */, + ); + path = Source; + sourceTree = SOURCE_ROOT; + }; + 11449A80249DD1B900E32D9F /* QiscusCoreApi */ = { + isa = PBXGroup; + children = ( + 11449A81249DD1B900E32D9F /* ConfigManager.swift */, + 11449A82249DD1B900E32D9F /* QiscusCore.swift */, + 11449A83249DD1B900E32D9F /* Network */, + 11449A92249DD1B900E32D9F /* Protocol */, + 11449A95249DD1B900E32D9F /* QiscusCoreAPI.h */, + 11449A96249DD1B900E32D9F /* Extentions */, + 11449A99249DD1B900E32D9F /* Model */, + 11449AA8249DD1B900E32D9F /* QiscusCoreAPI.modulemap */, + 11449AA9249DD1B900E32D9F /* SupportingFiles */, + 11449AAB249DD1B900E32D9F /* Helper */, + ); + path = QiscusCoreApi; + sourceTree = ""; + }; + 11449A83249DD1B900E32D9F /* Network */ = { + isa = PBXGroup; + children = ( + 11449A84249DD1B900E32D9F /* NetworkMessage.swift */, + 11449A85249DD1B900E32D9F /* NetworkRoom.swift */, + 11449A86249DD1B900E32D9F /* NetworkManager.swift */, + 11449A87249DD1B900E32D9F /* Encoding */, + 11449A8A249DD1B900E32D9F /* Service */, + ); + path = Network; + sourceTree = ""; + }; + 11449A87249DD1B900E32D9F /* Encoding */ = { + isa = PBXGroup; + children = ( + 11449A88249DD1B900E32D9F /* ParameterEncoder.swift */, + 11449A89249DD1B900E32D9F /* ParameterEncoding.swift */, + ); + path = Encoding; + sourceTree = ""; + }; + 11449A8A249DD1B900E32D9F /* Service */ = { + isa = PBXGroup; + children = ( + 11449A8B249DD1B900E32D9F /* HTTPUtil.swift */, + 11449A8C249DD1B900E32D9F /* Router.swift */, + 11449A8D249DD1B900E32D9F /* Endpoint */, + 11449A91249DD1B900E32D9F /* Endpoint.swift */, + ); + path = Service; + sourceTree = ""; + }; + 11449A8D249DD1B900E32D9F /* Endpoint */ = { + isa = PBXGroup; + children = ( + 11449A8E249DD1B900E32D9F /* EndpointComment.swift */, + 11449A8F249DD1B900E32D9F /* EndpointRoom.swift */, + 11449A90249DD1B900E32D9F /* EndpointUser.swift */, + ); + path = Endpoint; + sourceTree = ""; + }; + 11449A92249DD1B900E32D9F /* Protocol */ = { + isa = PBXGroup; + children = ( + 11449A93249DD1B900E32D9F /* Interface.swift */, + 11449A94249DD1B900E32D9F /* QiscusConnectionDelegate.swift */, + ); + path = Protocol; + sourceTree = ""; + }; + 11449A96249DD1B900E32D9F /* Extentions */ = { + isa = PBXGroup; + children = ( + 11449A97249DD1B900E32D9F /* Data.swift */, + 11449A98249DD1B900E32D9F /* UIDeviceInfo.swift */, + ); + path = Extentions; + sourceTree = ""; + }; + 11449A99249DD1B900E32D9F /* Model */ = { + isa = PBXGroup; + children = ( + 11449A9A249DD1B900E32D9F /* CommentModel.swift */, + 11449A9B249DD1B900E32D9F /* FileModel.swift */, + 11449A9C249DD1B900E32D9F /* Config */, + 11449A9E249DD1B900E32D9F /* NonceModel.swift */, + 11449A9F249DD1B900E32D9F /* LBModel.swift */, + 11449AA0249DD1B900E32D9F /* UserModel.swift */, + 11449AA1249DD1B900E32D9F /* QError.swift */, + 11449AA2249DD1B900E32D9F /* RoomEvent.swift */, + 11449AA3249DD1B900E32D9F /* CommentInfo.swift */, + 11449AA4249DD1B900E32D9F /* RoomModel.swift */, + 11449AA5249DD1B900E32D9F /* GenericModel.swift */, + 11449AA6249DD1B900E32D9F /* Base */, + ); + path = Model; + sourceTree = ""; + }; + 11449A9C249DD1B900E32D9F /* Config */ = { + isa = PBXGroup; + children = ( + 11449A9D249DD1B900E32D9F /* QiscusConfig.swift */, + ); + path = Config; + sourceTree = ""; + }; + 11449AA6249DD1B900E32D9F /* Base */ = { + isa = PBXGroup; + children = ( + 11449AA7249DD1B900E32D9F /* ApiResponse.swift */, + ); + path = Base; + sourceTree = ""; + }; + 11449AA9249DD1B900E32D9F /* SupportingFiles */ = { + isa = PBXGroup; + children = ( + 11449AAA249DD1B900E32D9F /* QiscusCoreAPI.modulemap */, + ); + path = SupportingFiles; + sourceTree = ""; + }; + 11449AAB249DD1B900E32D9F /* Helper */ = { + isa = PBXGroup; + children = ( + 11449AAC249DD1B900E32D9F /* QiscusLogger.swift */, + ); + path = Helper; + sourceTree = ""; + }; + 11449ACE249DD1D500E32D9F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 11449ACF249DD1D500E32D9F /* SwiftyJSON.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 11449A60249DD16B00E32D9F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 11449ABD249DD1B900E32D9F /* QiscusCoreAPI.h in Headers */, + 11449A76249DD16C00E32D9F /* QiscusCoreAPI.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 11449A64249DD16B00E32D9F /* QiscusCoreAPI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 11449A79249DD16C00E32D9F /* Build configuration list for PBXNativeTarget "QiscusCoreAPI" */; + buildPhases = ( + 11449A60249DD16B00E32D9F /* Headers */, + 11449A61249DD16B00E32D9F /* Sources */, + 11449A62249DD16B00E32D9F /* Frameworks */, + 11449A63249DD16B00E32D9F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = QiscusCoreAPI; + productName = QiscusCoreAPI; + productReference = 11449A65249DD16B00E32D9F /* QiscusCoreAPI.framework */; + productType = "com.apple.product-type.framework"; + }; + 11449A6D249DD16C00E32D9F /* QiscusCoreAPITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 11449A7C249DD16C00E32D9F /* Build configuration list for PBXNativeTarget "QiscusCoreAPITests" */; + buildPhases = ( + 11449A6A249DD16C00E32D9F /* Sources */, + 11449A6B249DD16C00E32D9F /* Frameworks */, + 11449A6C249DD16C00E32D9F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 11449A71249DD16C00E32D9F /* PBXTargetDependency */, + ); + name = QiscusCoreAPITests; + productName = QiscusCoreAPITests; + productReference = 11449A6E249DD16C00E32D9F /* QiscusCoreAPITests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 11449A5C249DD16B00E32D9F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 1140; + ORGANIZATIONNAME = Qiscus; + TargetAttributes = { + 11449A64249DD16B00E32D9F = { + CreatedOnToolsVersion = 11.4.1; + }; + 11449A6D249DD16C00E32D9F = { + CreatedOnToolsVersion = 11.4.1; + }; + }; + }; + buildConfigurationList = 11449A5F249DD16B00E32D9F /* Build configuration list for PBXProject "QiscusCoreAPI" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 11449A5B249DD16B00E32D9F; + productRefGroup = 11449A66249DD16B00E32D9F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 11449A64249DD16B00E32D9F /* QiscusCoreAPI */, + 11449A6D249DD16C00E32D9F /* QiscusCoreAPITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 11449A63249DD16B00E32D9F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 11449A6C249DD16C00E32D9F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 11449A61249DD16B00E32D9F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 11449AB7249DD1B900E32D9F /* EndpointComment.swift in Sources */, + 11449ACB249DD1B900E32D9F /* ApiResponse.swift in Sources */, + 11449AB4249DD1B900E32D9F /* ParameterEncoding.swift in Sources */, + 11449AC6249DD1B900E32D9F /* QError.swift in Sources */, + 11449ACA249DD1B900E32D9F /* GenericModel.swift in Sources */, + 11449AC3249DD1B900E32D9F /* NonceModel.swift in Sources */, + 11449AC4249DD1B900E32D9F /* LBModel.swift in Sources */, + 11449AB3249DD1B900E32D9F /* ParameterEncoder.swift in Sources */, + 11449ABB249DD1B900E32D9F /* Interface.swift in Sources */, + 11449ABA249DD1B900E32D9F /* Endpoint.swift in Sources */, + 11449AB8249DD1B900E32D9F /* EndpointRoom.swift in Sources */, + 11449AC5249DD1B900E32D9F /* UserModel.swift in Sources */, + 11449AAF249DD1B900E32D9F /* QiscusCore.swift in Sources */, + 11449AC1249DD1B900E32D9F /* FileModel.swift in Sources */, + 11449AC0249DD1B900E32D9F /* CommentModel.swift in Sources */, + 11449AB0249DD1B900E32D9F /* NetworkMessage.swift in Sources */, + 11449ABC249DD1B900E32D9F /* QiscusConnectionDelegate.swift in Sources */, + 11449AC7249DD1B900E32D9F /* RoomEvent.swift in Sources */, + 11449AB6249DD1B900E32D9F /* Router.swift in Sources */, + 11449AC8249DD1B900E32D9F /* CommentInfo.swift in Sources */, + 11449AB2249DD1B900E32D9F /* NetworkManager.swift in Sources */, + 11449ACC249DD1B900E32D9F /* QiscusLogger.swift in Sources */, + 11449AAE249DD1B900E32D9F /* ConfigManager.swift in Sources */, + 11449AC9249DD1B900E32D9F /* RoomModel.swift in Sources */, + 11449AB9249DD1B900E32D9F /* EndpointUser.swift in Sources */, + 11449AC2249DD1B900E32D9F /* QiscusConfig.swift in Sources */, + 11449AB1249DD1B900E32D9F /* NetworkRoom.swift in Sources */, + 11449AB5249DD1B900E32D9F /* HTTPUtil.swift in Sources */, + 11449ABE249DD1B900E32D9F /* Data.swift in Sources */, + 11449ABF249DD1B900E32D9F /* UIDeviceInfo.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 11449A6A249DD16C00E32D9F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 11449A74249DD16C00E32D9F /* QiscusCoreAPITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 11449A71249DD16C00E32D9F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 11449A64249DD16B00E32D9F /* QiscusCoreAPI */; + targetProxy = 11449A70249DD16C00E32D9F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 11449A77249DD16C00E32D9F /* 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; + }; + 11449A78249DD16C00E32D9F /* 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; + }; + 11449A7A249DD16C00E32D9F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = QiscusCoreAPI/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.qiscus.qismo.QiscusCoreAPI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 11449A7B249DD16C00E32D9F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = QiscusCoreAPI/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.qiscus.qismo.QiscusCoreAPI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 11449A7D249DD16C00E32D9F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = QiscusCoreAPITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.qiscus.qismo.QiscusCoreAPITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 11449A7E249DD16C00E32D9F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = QiscusCoreAPITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.qiscus.qismo.QiscusCoreAPITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 11449A5F249DD16B00E32D9F /* Build configuration list for PBXProject "QiscusCoreAPI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 11449A77249DD16C00E32D9F /* Debug */, + 11449A78249DD16C00E32D9F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 11449A79249DD16C00E32D9F /* Build configuration list for PBXNativeTarget "QiscusCoreAPI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 11449A7A249DD16C00E32D9F /* Debug */, + 11449A7B249DD16C00E32D9F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 11449A7C249DD16C00E32D9F /* Build configuration list for PBXNativeTarget "QiscusCoreAPITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 11449A7D249DD16C00E32D9F /* Debug */, + 11449A7E249DD16C00E32D9F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 11449A5C249DD16B00E32D9F /* Project object */; +} diff --git a/QiscusCoreAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/QiscusCoreAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..13ede5c --- /dev/null +++ b/QiscusCoreAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/QiscusCoreAPI.xcodeproj/project.xcworkspace/xcuserdata/rahardyan.xcuserdatad/UserInterfaceState.xcuserstate b/QiscusCoreAPI.xcodeproj/project.xcworkspace/xcuserdata/rahardyan.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..a214d7d Binary files /dev/null and b/QiscusCoreAPI.xcodeproj/project.xcworkspace/xcuserdata/rahardyan.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/QiscusCoreAPI.xcodeproj/xcshareddata/xcschemes/QiscusCoreAPI.xcscheme b/QiscusCoreAPI.xcodeproj/xcshareddata/xcschemes/QiscusCoreAPI.xcscheme new file mode 100644 index 0000000..124e2a6 --- /dev/null +++ b/QiscusCoreAPI.xcodeproj/xcshareddata/xcschemes/QiscusCoreAPI.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/QiscusCoreAPI.xcodeproj/xcuserdata/rahardyan.xcuserdatad/xcschemes/xcschememanagement.plist b/QiscusCoreAPI.xcodeproj/xcuserdata/rahardyan.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..8b98035 --- /dev/null +++ b/QiscusCoreAPI.xcodeproj/xcuserdata/rahardyan.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + QiscusCoreAPI.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/QiscusCoreAPI.xcworkspace/contents.xcworkspacedata b/QiscusCoreAPI.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..2ce4a1e --- /dev/null +++ b/QiscusCoreAPI.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/QiscusCoreAPI.xcworkspace/xcuserdata/rahardyan.xcuserdatad/UserInterfaceState.xcuserstate b/QiscusCoreAPI.xcworkspace/xcuserdata/rahardyan.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..b5685ad Binary files /dev/null and b/QiscusCoreAPI.xcworkspace/xcuserdata/rahardyan.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/QiscusCoreAPI/Info.plist b/QiscusCoreAPI/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/QiscusCoreAPI/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/QiscusCoreAPI/QiscusCoreAPI.h b/QiscusCoreAPI/QiscusCoreAPI.h new file mode 100644 index 0000000..714f4c0 --- /dev/null +++ b/QiscusCoreAPI/QiscusCoreAPI.h @@ -0,0 +1,19 @@ +// +// QiscusCoreAPI.h +// QiscusCoreAPI +// +// Created by Rahardyan Bisma on 20/06/20. +// Copyright © 2020 Qiscus. All rights reserved. +// + +#import + +//! Project version number for QiscusCoreAPI. +FOUNDATION_EXPORT double QiscusCoreAPIVersionNumber; + +//! Project version string for QiscusCoreAPI. +FOUNDATION_EXPORT const unsigned char QiscusCoreAPIVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/QiscusCoreAPITests/Info.plist b/QiscusCoreAPITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/QiscusCoreAPITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/QiscusCoreAPITests/QiscusCoreAPITests.swift b/QiscusCoreAPITests/QiscusCoreAPITests.swift new file mode 100644 index 0000000..cfe17f4 --- /dev/null +++ b/QiscusCoreAPITests/QiscusCoreAPITests.swift @@ -0,0 +1,34 @@ +// +// QiscusCoreAPITests.swift +// QiscusCoreAPITests +// +// Created by Rahardyan Bisma on 20/06/20. +// Copyright © 2020 Qiscus. All rights reserved. +// + +import XCTest +@testable import QiscusCoreAPI + +class QiscusCoreAPITests: 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. + } + } + +} diff --git a/QiscusCoreApi.framework/Headers/QiscusCoreApi-Swift.h b/QiscusCoreApi.framework/Headers/QiscusCoreApi-Swift.h deleted file mode 100644 index 0b1f6ca..0000000 --- a/QiscusCoreApi.framework/Headers/QiscusCoreApi-Swift.h +++ /dev/null @@ -1,215 +0,0 @@ -// Generated by Apple Swift version 5.2.2 effective-4.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgcc-compat" - -#if !defined(__has_include) -# define __has_include(x) 0 -#endif -#if !defined(__has_attribute) -# define __has_attribute(x) 0 -#endif -#if !defined(__has_feature) -# define __has_feature(x) 0 -#endif -#if !defined(__has_warning) -# define __has_warning(x) 0 -#endif - -#if __has_include() -# include -#endif - -#pragma clang diagnostic ignored "-Wauto-import" -#include -#include -#include -#include - -#if !defined(SWIFT_TYPEDEFS) -# define SWIFT_TYPEDEFS 1 -# if __has_include() -# include -# elif !defined(__cplusplus) -typedef uint_least16_t char16_t; -typedef uint_least32_t char32_t; -# endif -typedef float swift_float2 __attribute__((__ext_vector_type__(2))); -typedef float swift_float3 __attribute__((__ext_vector_type__(3))); -typedef float swift_float4 __attribute__((__ext_vector_type__(4))); -typedef double swift_double2 __attribute__((__ext_vector_type__(2))); -typedef double swift_double3 __attribute__((__ext_vector_type__(3))); -typedef double swift_double4 __attribute__((__ext_vector_type__(4))); -typedef int swift_int2 __attribute__((__ext_vector_type__(2))); -typedef int swift_int3 __attribute__((__ext_vector_type__(3))); -typedef int swift_int4 __attribute__((__ext_vector_type__(4))); -typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); -typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); -typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); -#endif - -#if !defined(SWIFT_PASTE) -# define SWIFT_PASTE_HELPER(x, y) x##y -# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) -#endif -#if !defined(SWIFT_METATYPE) -# define SWIFT_METATYPE(X) Class -#endif -#if !defined(SWIFT_CLASS_PROPERTY) -# if __has_feature(objc_class_property) -# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ -# else -# define SWIFT_CLASS_PROPERTY(...) -# endif -#endif - -#if __has_attribute(objc_runtime_name) -# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) -#else -# define SWIFT_RUNTIME_NAME(X) -#endif -#if __has_attribute(swift_name) -# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) -#else -# define SWIFT_COMPILE_NAME(X) -#endif -#if __has_attribute(objc_method_family) -# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) -#else -# define SWIFT_METHOD_FAMILY(X) -#endif -#if __has_attribute(noescape) -# define SWIFT_NOESCAPE __attribute__((noescape)) -#else -# define SWIFT_NOESCAPE -#endif -#if __has_attribute(ns_consumed) -# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) -#else -# define SWIFT_RELEASES_ARGUMENT -#endif -#if __has_attribute(warn_unused_result) -# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -# define SWIFT_WARN_UNUSED_RESULT -#endif -#if __has_attribute(noreturn) -# define SWIFT_NORETURN __attribute__((noreturn)) -#else -# define SWIFT_NORETURN -#endif -#if !defined(SWIFT_CLASS_EXTRA) -# define SWIFT_CLASS_EXTRA -#endif -#if !defined(SWIFT_PROTOCOL_EXTRA) -# define SWIFT_PROTOCOL_EXTRA -#endif -#if !defined(SWIFT_ENUM_EXTRA) -# define SWIFT_ENUM_EXTRA -#endif -#if !defined(SWIFT_CLASS) -# if __has_attribute(objc_subclassing_restricted) -# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA -# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA -# else -# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA -# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA -# endif -#endif -#if !defined(SWIFT_RESILIENT_CLASS) -# if __has_attribute(objc_class_stub) -# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) -# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) -# else -# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) -# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) -# endif -#endif - -#if !defined(SWIFT_PROTOCOL) -# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA -# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA -#endif - -#if !defined(SWIFT_EXTENSION) -# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) -#endif - -#if !defined(OBJC_DESIGNATED_INITIALIZER) -# if __has_attribute(objc_designated_initializer) -# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) -# else -# define OBJC_DESIGNATED_INITIALIZER -# endif -#endif -#if !defined(SWIFT_ENUM_ATTR) -# if defined(__has_attribute) && __has_attribute(enum_extensibility) -# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) -# else -# define SWIFT_ENUM_ATTR(_extensibility) -# endif -#endif -#if !defined(SWIFT_ENUM) -# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type -# if __has_feature(generalized_swift_name) -# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type -# else -# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) -# endif -#endif -#if !defined(SWIFT_UNAVAILABLE) -# define SWIFT_UNAVAILABLE __attribute__((unavailable)) -#endif -#if !defined(SWIFT_UNAVAILABLE_MSG) -# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) -#endif -#if !defined(SWIFT_AVAILABILITY) -# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) -#endif -#if !defined(SWIFT_WEAK_IMPORT) -# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) -#endif -#if !defined(SWIFT_DEPRECATED) -# define SWIFT_DEPRECATED __attribute__((deprecated)) -#endif -#if !defined(SWIFT_DEPRECATED_MSG) -# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) -#endif -#if __has_feature(attribute_diagnose_if_objc) -# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) -#else -# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) -#endif -#if !defined(IBSegueAction) -# define IBSegueAction -#endif -#if __has_feature(modules) -#if __has_warning("-Watimport-in-framework-header") -#pragma clang diagnostic ignored "-Watimport-in-framework-header" -#endif -@import Foundation; -@import UIKit; -#endif - -#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" -#pragma clang diagnostic ignored "-Wduplicate-method-arg" -#if __has_warning("-Wpragma-clang-attribute") -# pragma clang diagnostic ignored "-Wpragma-clang-attribute" -#endif -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma clang diagnostic ignored "-Wnullability" - -#if __has_attribute(external_source_symbol) -# pragma push_macro("any") -# undef any -# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="QiscusCoreApi",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) -# pragma pop_macro("any") -#endif - - - - - -#if __has_attribute(external_source_symbol) -# pragma clang attribute pop -#endif -#pragma clang diagnostic pop diff --git a/QiscusCoreApi.framework/Info.plist b/QiscusCoreApi.framework/Info.plist deleted file mode 100644 index 4d13b76..0000000 Binary files a/QiscusCoreApi.framework/Info.plist and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm.swiftdoc b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm.swiftdoc deleted file mode 100644 index 19efd36..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm.swiftdoc and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm.swiftmodule b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm.swiftmodule deleted file mode 100644 index 4392c55..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm.swiftmodule and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm64.swiftdoc b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm64.swiftdoc deleted file mode 100644 index 3cb73dc..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm64.swiftdoc and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm64.swiftmodule b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm64.swiftmodule deleted file mode 100644 index b16b00c..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/arm64.swiftmodule and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/i386.swiftdoc b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/i386.swiftdoc deleted file mode 100644 index 0e6c699..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/i386.swiftdoc and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/i386.swiftmodule b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/i386.swiftmodule deleted file mode 100644 index 9a00a39..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/i386.swiftmodule and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/x86_64.swiftdoc b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/x86_64.swiftdoc deleted file mode 100644 index b31b6ae..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/x86_64.swiftdoc and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/x86_64.swiftmodule b/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/x86_64.swiftmodule deleted file mode 100644 index 40f31a2..0000000 Binary files a/QiscusCoreApi.framework/Modules/QiscusCoreApi.swiftmodule/x86_64.swiftmodule and /dev/null differ diff --git a/QiscusCoreApi.framework/Modules/module.modulemap b/QiscusCoreApi.framework/Modules/module.modulemap deleted file mode 100644 index 71588c8..0000000 --- a/QiscusCoreApi.framework/Modules/module.modulemap +++ /dev/null @@ -1,11 +0,0 @@ -framework module QiscusCoreApi { - umbrella header "QiscusCoreApi.h" - - export * - module * { export * } -} - -module QiscusCoreApi.Swift { - header "QiscusCoreApi-Swift.h" - requires objc -} diff --git a/QiscusCoreApi.framework/QiscusCoreApi b/QiscusCoreApi.framework/QiscusCoreApi deleted file mode 100755 index ad65afc..0000000 Binary files a/QiscusCoreApi.framework/QiscusCoreApi and /dev/null differ diff --git a/QiscusCoreApi.framework/_CodeSignature/CodeResources b/QiscusCoreApi.framework/_CodeSignature/CodeResources deleted file mode 100644 index 7ba20d8..0000000 --- a/QiscusCoreApi.framework/_CodeSignature/CodeResources +++ /dev/null @@ -1,207 +0,0 @@ - - - - - files - - Headers/QiscusCoreApi-Swift.h - - aYEHyo0NQYu7vBCEjrp1PJaGZD0= - - Headers/QiscusCoreApi.h - - PBd9xkDqWX76IYa8z8bElZEzgaE= - - Info.plist - - /8+RrEctf63ka/WAWwN8YL+lOwY= - - Modules/QiscusCoreApi.swiftmodule/arm.swiftdoc - - slVmLkKxOiT+q9WFY4v4cbfkKYo= - - Modules/QiscusCoreApi.swiftmodule/arm.swiftmodule - - W0jUhpjs88qsV8vl95IKKx9grFg= - - Modules/QiscusCoreApi.swiftmodule/arm64.swiftdoc - - WE+TTYC8sdiqvcfTX2eXpdSbf6I= - - Modules/QiscusCoreApi.swiftmodule/arm64.swiftmodule - - wEqxEW4ZbifSm+54wboK7toeE8M= - - Modules/module.modulemap - - nxOu+3VFzi9wZbI+Z66xMFsnGNM= - - - files2 - - Headers/QiscusCoreApi-Swift.h - - hash - - aYEHyo0NQYu7vBCEjrp1PJaGZD0= - - hash2 - - E9irwjb4HFTMS+w/oX3PG1fYaTPz84aeZPX3qHpvmX4= - - - Headers/QiscusCoreApi.h - - hash - - PBd9xkDqWX76IYa8z8bElZEzgaE= - - hash2 - - o5H3vEX8DoH21mIKEAszdTXaFlvoW8iLQ1SQGTL+/Zo= - - - Modules/QiscusCoreApi.swiftmodule/arm.swiftdoc - - hash - - slVmLkKxOiT+q9WFY4v4cbfkKYo= - - hash2 - - zp021/fc30nWP3dfW8+E77G8zOj+6606o0+lde10Ujc= - - - Modules/QiscusCoreApi.swiftmodule/arm.swiftmodule - - hash - - W0jUhpjs88qsV8vl95IKKx9grFg= - - hash2 - - GbCelIotiy4/NJKeO7l4cDvtU/kBpNg6Q3GeE+Pw78s= - - - Modules/QiscusCoreApi.swiftmodule/arm64.swiftdoc - - hash - - WE+TTYC8sdiqvcfTX2eXpdSbf6I= - - hash2 - - nKMHHLz1v2Ve23lOBUvukDrgsgaKB+WGpK0YveKnhDs= - - - Modules/QiscusCoreApi.swiftmodule/arm64.swiftmodule - - hash - - wEqxEW4ZbifSm+54wboK7toeE8M= - - hash2 - - DGLHo8Bt/erHwx7xxH4Rpp5MJM9eFTSMss57Z1sP+G8= - - - Modules/module.modulemap - - hash - - nxOu+3VFzi9wZbI+Z66xMFsnGNM= - - hash2 - - uVhHw9ZLYwRVeUMrfLcu86NJ3Vh6w2U6IoCchl2dZAY= - - - - rules - - ^.* - - ^.*\.lproj/ - - optional - - weight - 1000 - - ^.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Base\.lproj/ - - weight - 1010 - - ^version.plist$ - - - rules2 - - .*\.dSYM($|/) - - weight - 11 - - ^(.*/)?\.DS_Store$ - - omit - - weight - 2000 - - ^.* - - ^.*\.lproj/ - - optional - - weight - 1000 - - ^.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Base\.lproj/ - - weight - 1010 - - ^Info\.plist$ - - omit - - weight - 20 - - ^PkgInfo$ - - omit - - weight - 20 - - ^embedded\.provisionprofile$ - - weight - 20 - - ^version\.plist$ - - weight - 20 - - - - diff --git a/Source/QiscusCoreApi/ConfigManager.swift b/Source/QiscusCoreApi/ConfigManager.swift new file mode 100644 index 0000000..d82d7b3 --- /dev/null +++ b/Source/QiscusCoreApi/ConfigManager.swift @@ -0,0 +1,13 @@ +// +// ConfigManager.swift +// Pods +// +// Created by Qiscus on 07/08/18. +// + +import Foundation + +public struct QiscusConfig { + public let appId: String + public var server: QiscusServer +} diff --git a/Source/QiscusCoreApi/Extentions/Data.swift b/Source/QiscusCoreApi/Extentions/Data.swift new file mode 100644 index 0000000..40648c7 --- /dev/null +++ b/Source/QiscusCoreApi/Extentions/Data.swift @@ -0,0 +1,16 @@ +// +// Data.swift +// QiscusCoreLite +// +// Created by asharijuang on 28/01/20. +// + +import Foundation + +extension Data { + func toJsonString() -> String { + guard let jsonString = String(data: self, encoding: .utf8) else {return "invalid json data"} + + return jsonString + } +} diff --git a/Source/QiscusCoreApi/Extentions/UIDeviceInfo.swift b/Source/QiscusCoreApi/Extentions/UIDeviceInfo.swift new file mode 100644 index 0000000..6519fbd --- /dev/null +++ b/Source/QiscusCoreApi/Extentions/UIDeviceInfo.swift @@ -0,0 +1,79 @@ +// +// UIDeviceInfo.swift +// QiscusCoreLite +// +// Created by Qiscus on 26/03/19. +// Copyright © 2019 Qiscus. All rights reserved. +// + +#if os(iOS) +import UIKit +#endif +import Foundation + +public extension UIDevice { + + static let modelName: String = { + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let identifier = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + + func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity + #if os(iOS) + switch identifier { + case "iPod5,1": return "iPod Touch 5" + case "iPod7,1": return "iPod Touch 6" + case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" + case "iPhone4,1": return "iPhone 4s" + case "iPhone5,1", "iPhone5,2": return "iPhone 5" + case "iPhone5,3", "iPhone5,4": return "iPhone 5c" + case "iPhone6,1", "iPhone6,2": return "iPhone 5s" + case "iPhone7,2": return "iPhone 6" + case "iPhone7,1": return "iPhone 6 Plus" + case "iPhone8,1": return "iPhone 6s" + case "iPhone8,2": return "iPhone 6s Plus" + case "iPhone9,1", "iPhone9,3": return "iPhone 7" + case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" + case "iPhone8,4": return "iPhone SE" + case "iPhone10,1", "iPhone10,4": return "iPhone 8" + case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" + case "iPhone10,3", "iPhone10,6": return "iPhone X" + case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2" + case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3" + case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4" + case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" + case "iPad5,3", "iPad5,4": return "iPad Air 2" + case "iPad6,11", "iPad6,12": return "iPad 5" + case "iPad7,5", "iPad7,6": return "iPad 6" + case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini" + case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2" + case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3" + case "iPad5,1", "iPad5,2": return "iPad Mini 4" + case "iPad6,3", "iPad6,4": return "iPad Pro 9.7 Inch" + case "iPad6,7", "iPad6,8": return "iPad Pro 12.9 Inch" + case "iPad7,1", "iPad7,2": return "iPad Pro 12.9 Inch 2. Generation" + case "iPad7,3", "iPad7,4": return "iPad Pro 10.5 Inch" + case "AppleTV5,3": return "Apple TV" + case "AppleTV6,2": return "Apple TV 4K" + case "AudioAccessory1,1": return "HomePod" + case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))" + default: return identifier + } + #elseif os(tvOS) + switch identifier { + case "AppleTV5,3": return "Apple TV 4" + case "AppleTV6,2": return "Apple TV 4K" + case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))" + default: return identifier + } + #endif + } + + return mapToDevice(identifier: identifier) + }() + +} diff --git a/Source/QiscusCoreApi/Helper/QiscusLogger.swift b/Source/QiscusCoreApi/Helper/QiscusLogger.swift new file mode 100644 index 0000000..6a9bc4b --- /dev/null +++ b/Source/QiscusCoreApi/Helper/QiscusLogger.swift @@ -0,0 +1,80 @@ +// +// QiscusLogger.swift +// QiscusCoreLite +// +// Created by Rahardyan Bisma on 24/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +import Foundation + +class QiscusLogger { + static func debugPrint(_ text: String) { + if QiscusCoreAPI.enableDebugPrint { + print("[QiscusCoreLite] \(text)") + } + } + + static func debugDBPrint(_ text: String) { + if QiscusCoreAPI.enableDebugPrint { + print("[QiscusCoreLiteDB] \(text)") + } + } + + static func errorPrint(_ text: String) { + if QiscusCoreAPI.enableDebugPrint { + print("[QiscusCoreLite] Error: \(text)") + } + } + + static func networkLogger(request: URLRequest) { + if !QiscusCoreAPI.enableDebugPrint { + return + } + + print("\n ====================> REQUEST <============ \n") + defer { print("\n ====================> END REQUEST <============ \n") } + + let urlAsString = request.url?.absoluteString ?? "" + let urlComponents = NSURLComponents(string: urlAsString) + + let method = request.httpMethod != nil ? "\(request.httpMethod ?? "")" : "" + let path = "\(urlComponents?.path ?? "")" + let query = "\(urlComponents?.query ?? "")" + let host = "\(urlComponents?.host ?? "")" + + var logOutput = """ + \(urlAsString) \n\n + \(method) \(path)?\(query) HTTP/1.1 \n + HOST: \(host)\n + """ + for (key,value) in request.allHTTPHeaderFields ?? [:] { + logOutput += "\(key): \(value) \n" + } + if let body = request.httpBody { + logOutput += "\n \(NSString(data: body, encoding: String.Encoding.utf8.rawValue) ?? "")" + } + + print(logOutput) + } + + static func networkLogger(request: URLRequest, response: Data?) { + if !QiscusCoreAPI.enableDebugPrint { + return + } + + print("\n ====================> RESPONSE <============ \n") + defer { print("\n ====================> END RESPONSE <============ \n") } + + let urlAsString = request.url?.absoluteString ?? "" + var responseMessage = "" + if let responseData = response { + responseMessage = responseData.toJsonString() + } + let logOutput = """ + URL: \(urlAsString) \n + Response: \(responseMessage) + """ + print(logOutput) + } +} diff --git a/Source/QiscusCoreApi/Info.plist b/Source/QiscusCoreApi/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Source/QiscusCoreApi/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Source/QiscusCoreApi/Model/Base/ApiResponse.swift b/Source/QiscusCoreApi/Model/Base/ApiResponse.swift new file mode 100644 index 0000000..5f6a0ae --- /dev/null +++ b/Source/QiscusCoreApi/Model/Base/ApiResponse.swift @@ -0,0 +1,225 @@ +// +// ApiResponse.swift +// QiscusCoreLite +// +// Created by Rahardyan Bisma on 26/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +import Foundation +import SwiftyJSON + +class ApiResponse { + static func decode(from data: Data) -> JSON { + let json = JSON(data) + let result = json["results"] + return result + } + + static func decodeWithoutResult(from data: Data) -> JSON { + let json = JSON(data) + return json + } + + static func decode(string data: String) -> JSON { + let json = JSON.init(parseJSON: data) + return json + } + + static func decode(unread data: Data) -> Int { + let json = JSON(data) + let unread = json["results"]["total_unread_count"].intValue + return unread + } + + static func decode(syncEvent event: Data) -> [SyncEvent] { + let json = JSON(event) + var results = [SyncEvent]() + if let result = json["events"].array { + result.forEach { (data) in + let event = SyncEvent(json: data) + results.append(event) + } + return results + }else { + return results + } + } + + static func decodeError(from data: Data){ + let json = JSON(data) + let error = json["error"]["detailed_messages"].arrayObject + } +} + +class FileApiResponse { + static func upload(from json: JSON) -> FileModel { + let i = json["file"] + return FileModel(json: i) + } +} + +class UserApiResponse { + static func blockedUsers(from json: JSON) -> [MemberModel]? { + if let rooms = json["blocked_user"].array { + var results = [MemberModel]() + for room in rooms { + let data = MemberModel(json: room) + results.append(data) + } + return results + }else { + return nil + } + } + + static func user(from json: JSON) -> UserModel { + let comment = json["user"] + return UserModel(json: comment) + } + + static func allUser(from json: JSON) -> [MemberModel]? { + if let rooms = json["users"].array { + var results = [MemberModel]() + for room in rooms { + let data = MemberModel(json: room) + results.append(data) + } + return results + }else { + return nil + } + } + + static func blockUser(from json: JSON) -> MemberModel { + let comment = json["user"] + return MemberModel(json: comment) + } + + static func meta(from json: JSON) -> Meta { + let meta = json["meta"] + return Meta(json: meta) + } +} + +class RoomApiResponse { + static func rooms(from json: JSON) -> [RoomModel]? { + if let rooms = json["rooms_info"].array { + var results = [RoomModel]() + for room in rooms { + let data = RoomModel(json: room) + results.append(data) + } + return results + }else { + return nil + } + } + + static func room(from json: JSON) -> RoomModel { + let comment = json["room"] + return RoomModel(json: comment) + } + + static func meta(from json: JSON) -> Meta { + let meta = json["meta"] + return Meta(json: meta) + } + + static func metaRoomParticipant(from json: JSON) -> MetaRoomParticipant { + let meta = json["meta"] + return MetaRoomParticipant(json: meta) + } + + static func addParticipants(from json: JSON) -> [MemberModel]? { + if let members = json["participants_added"].array { + var results = [MemberModel]() + for member in members { + let data = MemberModel(json: member) + results.append(data) + } + return results + }else { + return nil + } + } + + static func participants(from json: JSON) -> [MemberModel]? { + if let members = json["participants"].array { + var results = [MemberModel]() + for member in members { + let data = MemberModel(json: member) + results.append(data) + } + return results + }else { + return nil + } + } + +} + +class CommentApiResponse { + static func comments(from json: JSON) -> [CommentModel]? { + if let comments = json["comments"].array { + var results = [CommentModel]() + for comment in comments { + let data = CommentModel(json: comment) + results.append(data) + } + return results + }else { + return nil + } + } + + static func comment(from json: JSON) -> CommentModel { + let comment = json["comment"] + return CommentModel(json: comment) + } + + static func commentDeliveredUser(from json: JSON) -> [MemberModel]? { + if let members = json["delivered_to"].array { + var results = [MemberModel]() + for member in members { + let user = member["user"] + let data = MemberModel(json: user) + + results.append(data) + } + return results + }else { + return nil + } + } + + static func commentReadUser(from json: JSON) -> [MemberModel]? { + if let members = json["read_by"].array { + var results = [MemberModel]() + for member in members { + let user = member["user"] + let data = MemberModel(json: user) + + results.append(data) + } + return results + }else { + return nil + } + } + + static func commentPendingUser(from json: JSON) -> [MemberModel]? { + if let members = json["pending"].array { + var results = [MemberModel]() + for member in members { + let user = member["user"] + let data = MemberModel(json: user) + results.append(data) + } + return results + }else { + return nil + } + } + +} diff --git a/Source/QiscusCoreApi/Model/CommentInfo.swift b/Source/QiscusCoreApi/Model/CommentInfo.swift new file mode 100644 index 0000000..edbd139 --- /dev/null +++ b/Source/QiscusCoreApi/Model/CommentInfo.swift @@ -0,0 +1,16 @@ +// +// CommentInfo.swift +// QiscusCoreLite +// +// Created by Qiscus on 23/01/19. +// + +import Foundation + +public struct CommentInfo { + public var comment : CommentModel? + public var deliveredUser = [MemberModel]() + public var readUser = [MemberModel]() + public var sentUser = [MemberModel]() + +} diff --git a/Source/QiscusCoreApi/Model/CommentModel.swift b/Source/QiscusCoreApi/Model/CommentModel.swift new file mode 100644 index 0000000..52c4be1 --- /dev/null +++ b/Source/QiscusCoreApi/Model/CommentModel.swift @@ -0,0 +1,188 @@ +// +// CommentModel.swift +// QiscusCoreLite +// +// Created by Rahardyan Bisma on 26/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +import Foundation +import SwiftyJSON + +public class SyncMeta { + public let last_received_comment_id : Int? = nil + public let need_clear : Bool? = nil +} + +open class CommentModel { + //public var onChange : (CommentModel) -> Void = { _ in} // data binding + public internal(set) var commentBeforeId : String = "" + public internal(set) var id : String = "" + public internal(set) var isDeleted : Bool = false + public internal(set) var isPublicChannel : Bool = false + public var status : CommentStatus = .sending + public var message : String = "" + /// Comment payload, to describe comment type. + public var payload : [String:Any]? = nil + /// Extra data, set after comment is complate. + public var extras : [String:Any]? = nil + public var roomId : String = "" + public internal(set) var timestamp : String = "" + public var type : String = "text" + public internal(set) var uniqId : String = "" + public internal(set) var unixTimestamp : Int64 = 0 + public internal(set) var userAvatarUrl : URL? = nil + public internal(set) var userId : String = "" + public internal(set) var username : String = "" + public internal(set) var userEmail : String = "" + /// automatic set when comment initiated + public var date : Date { + get { + return self.getDate() + } + } + + init(user: UserModel) { + self.userId = String(user.id) + self.username = user.username + self.userAvatarUrl = user.avatarUrl + self.userEmail = user.email + let now = Int64(NSDate().timeIntervalSince1970 * 1000000000.0) // nano sec + self.uniqId = "ios_\(now)" + self.id = "ios_\(now)" + self.unixTimestamp = now + self.timestamp = CommentModel.getTimestamp() + } + + init(json: JSON) { + self.id = json["id_str"].stringValue + self.roomId = json["room_id_str"].stringValue + self.uniqId = json["unique_temp_id"].stringValue + self.commentBeforeId = json["comment_before_id_str"].stringValue + self.userEmail = json["email"].stringValue + self.isDeleted = json["is_deleted"].boolValue + self.isPublicChannel = json["is_public_channel"].boolValue + self.message = json["message"].stringValue + self.payload = json["payload"].dictionaryObject + self.timestamp = json["timestamp"].stringValue + self.unixTimestamp = json["unix_nano_timestamp"].int64Value + self.userAvatarUrl = json["user_avatar_url"].url ?? json["user_avatar"].url ?? URL(string: "http://") + self.username = json["username"].stringValue + self.userId = json["user_id_str"].stringValue + let _status = json["status"].stringValue + for s in CommentStatus.all { + if s.rawValue == _status { + self.status = s + } + } + if isDeleted { + self.status = .deleted // maping status deleted, backend not provide + } + let _type = json["type"].stringValue + if _type.lowercased() != "custom" { + self.type = _type + }else { + self.type = getType(fromPayload: self.payload) + // parsing payload + if let _payload = self.payload { + self.payload?.removeAll() + self.payload = getPayload(fromPayload: _payload) + } + } + + self.extras = json["extras"].dictionaryObject + } +} + +extension CommentModel { + private func getType(fromPayload data: [String:Any]?) -> String { + guard let payload = data else { return "custom"} + let type = payload["type"] as! String + return type + } + + private func getPayload(fromPayload data: [String:Any]) -> [String:Any]? { + if let payload = data["content"] as? [String:Any]{ + if !payload.isEmpty { + return payload + }else { return nil } + }else { + return nil + } + } + + func getDate() -> Date { + //let timezone = TimeZone.current.identifier + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + //formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.timeZone = TimeZone(abbreviation: "UTC") + formatter.locale = Locale(identifier: "en_US_POSIX") + let date = formatter.date(from: self.timestamp) + return date ?? Date() + } + + static func getTimestamp() -> String { + let timezone = TimeZone.current.identifier + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + //formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.timeZone = TimeZone(abbreviation: "UTC") + formatter.locale = Locale(identifier: "en_US_POSIX") + return formatter.string(from: Date()) + } + + func isQiscustype() -> Bool { + var result = false + for t in CommentType.all { + if self.type == t.rawValue { + result = true + } + } + return result + } +} + +public enum CommentStatus : String, CaseIterable { + case sending = "sending" + case pending = "pending" + case failed = "failed" + case sent = "sent" + case delivered = "delivered" + case read = "read" + case deleting = "deleting" // because delete process not only in device + case deleted = "deleted" + + static let all = [sending, pending, failed, sent, delivered, read, deleted] + + var intValue : Int { + get { + return self.asInt() + } + } + private func asInt() -> Int { + for (index,s) in CommentStatus.all.enumerated() { + if self == s { + return index + } + } + return 0 + } +} + +enum CommentType: String { + case text = "text" + case fileAttachment = "file_attachment" + case accountLink = "account_linking" + case buttons = "buttons" + case buttonPostbackResponse = "button_postback_response" + case reply = "reply" + case systemEvent = "system_event" + case card = "card" + case custom = "custom" + case location = "location" + case contactPerson = "contact_person" + case carousel = "carousel" + + static let all = [text,fileAttachment,accountLink,buttons,buttonPostbackResponse,reply,systemEvent,card,custom,location,contactPerson,carousel] +} diff --git a/Source/QiscusCoreApi/Model/Config/QiscusConfig.swift b/Source/QiscusCoreApi/Model/Config/QiscusConfig.swift new file mode 100644 index 0000000..3cdab7c --- /dev/null +++ b/Source/QiscusCoreApi/Model/Config/QiscusConfig.swift @@ -0,0 +1,60 @@ +// +// QiscusConfig.swift +// QiscusCoreLite +// +// Created by Qiscus on 07/08/18. +// + +import Foundation + +public struct QiscusServer { + public let url : URL + public let realtimeURL : String? + public let realtimePort : UInt16? + public let brokerLBUrl : String? + + public init (url: URL, realtimeURL: String?, realtimePort: UInt16?, brokerLBUrl: String? = nil) { + //check for urlServerApi + if (url.absoluteString.range(of: "/api/v2/mobile") == nil){ + self.url = url.appendingPathComponent("/api/v2/mobile") + }else{ + self.url = url + } + + //check for realtimeURL + if let realtimeURL = realtimeURL{ + if (realtimeURL.range(of: "ssl://") != nil){ + let urlFull = realtimeURL.components(separatedBy: "ssl://") + let ssl = urlFull[0] // ssl:// + let baseRealtimeUrl = urlFull[1] //qiscus-mqtt.api.halodoc.com:1885 or qiscus-mqtt.api.halodoc.com + + //check again + if (baseRealtimeUrl.range(of: ":") != nil){ + let checkBase = baseRealtimeUrl.components(separatedBy: ":") + let realtimeURL = checkBase[0] //qiscus-mqtt.api.halodoc.com + self.realtimeURL = realtimeURL + }else{ + self.realtimeURL = baseRealtimeUrl + } + }else{ + if (realtimeURL.range(of: ":") != nil){ + let checkBase = realtimeURL.components(separatedBy: ":") + let realtimeURL = checkBase[0] //qiscus-mqtt.api.halodoc.com + self.realtimeURL = realtimeURL + }else{ + self.realtimeURL = realtimeURL + } + } + }else{ + self.realtimeURL = realtimeURL + } + + self.realtimePort = realtimePort + if let urlBroker = brokerLBUrl { + self.brokerLBUrl = urlBroker + }else{ + self.brokerLBUrl = nil + } + + } +} diff --git a/Source/QiscusCoreApi/Model/FileModel.swift b/Source/QiscusCoreApi/Model/FileModel.swift new file mode 100644 index 0000000..305dbb5 --- /dev/null +++ b/Source/QiscusCoreApi/Model/FileModel.swift @@ -0,0 +1,66 @@ +// +// FileModel.swift +// QiscusCoreLite +// +// Created by Qiscus on 06/09/18. +// + +/** + file = { + name = "upload1.jpg"; + pages = 1; + size = 2128079; + url = "https://upload1.jpg"; + } + */ + +import SwiftyJSON +import Foundation + +public struct FileModel { + public var name : String = "" + public var size : Int = 1 + public var url : URL + var downloaded : Bool = false + + public init(url: URL) { + self.url = url + } + + init(json: JSON) { + url = json["url"].url ?? URL(string: "http://")! + name = json["name"].stringValue + size = json["size"].intValue + } +} + +open class FileUploadModel { + public var data : Data? + public var name : String = "" + public var caption : String = "" + + public init() { + + } +} + +class Download { + + var file: FileModel + init(file: FileModel) { + self.file = file + } + + // Download service sets these values: + var task: URLSessionDownloadTask? + var isDownloading = false + var resumeData: Data? + var totalBytes: Int64 = 0 + + // Download delegate sets this value: + var progress: Float = 0 + + // Download progress bind + var onProgress : (Float) -> Void = { _ in} + var onCompleted : (Bool) -> Void = { _ in} +} diff --git a/Source/QiscusCoreApi/Model/GenericModel.swift b/Source/QiscusCoreApi/Model/GenericModel.swift new file mode 100644 index 0000000..8b8873e --- /dev/null +++ b/Source/QiscusCoreApi/Model/GenericModel.swift @@ -0,0 +1,26 @@ +// +// GenericModel.swift +// QiscusCoreLite +// +// Created by Qiscus on 13/08/18. +// + +import Foundation + +/// Delete type +/// +/// - forMe: Delete only for my account +/// - forEveryone: Deleted message for everyone +public enum DeleteType { + case forMe + case forEveryone +} + +/// Delete source +/// +/// - hard: Message totaly deleted from server +/// - soft: Soft delete this message still on there but content is changes "message has beed deleted" +public enum DeleteSource { + case hard + case soft +} diff --git a/Source/QiscusCoreApi/Model/LBModel.swift b/Source/QiscusCoreApi/Model/LBModel.swift new file mode 100644 index 0000000..ac9646b --- /dev/null +++ b/Source/QiscusCoreApi/Model/LBModel.swift @@ -0,0 +1,17 @@ +// +// LBModel.swift +// Alamofire +// +// Created by Qiscus on 21/10/19. +// + +import Foundation +import SwiftyJSON + +public class LBModel { + public let node : String + + init(json: JSON) { + self.node = json["node"].stringValue + } +} diff --git a/Source/QiscusCoreApi/Model/NonceModel.swift b/Source/QiscusCoreApi/Model/NonceModel.swift new file mode 100644 index 0000000..9b5ee3c --- /dev/null +++ b/Source/QiscusCoreApi/Model/NonceModel.swift @@ -0,0 +1,20 @@ +// +// NonceModel.swift +// QiscusCoreLite +// +// Created by Rahardyan Bisma on 24/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +import Foundation +import SwiftyJSON + +public class QNonce { + public let expiredAt : Int + public let nonce : String + + init(json: JSON) { + self.expiredAt = json["expired_at"].intValue + self.nonce = json["nonce"].stringValue + } +} diff --git a/Source/QiscusCoreApi/Model/QError.swift b/Source/QiscusCoreApi/Model/QError.swift new file mode 100644 index 0000000..651c37e --- /dev/null +++ b/Source/QiscusCoreApi/Model/QError.swift @@ -0,0 +1,20 @@ +// +// QError.swift +// QiscusCoreLite +// +// Created by Qiscus on 27/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +#if os(iOS) +import UIKit +#endif + +public class QError { + public var message : String = "" + + init(message: String) { + self.message = message + } + +} diff --git a/Source/QiscusCoreApi/Model/RoomEvent.swift b/Source/QiscusCoreApi/Model/RoomEvent.swift new file mode 100644 index 0000000..af77a07 --- /dev/null +++ b/Source/QiscusCoreApi/Model/RoomEvent.swift @@ -0,0 +1,101 @@ +// +// RoomEvent.swift +// QiscusCoreLite +// +// Created by Qiscus on 29/10/18. +// + +import Foundation +import SwiftyJSON + +public struct RoomEvent { + public let sender : String + public let data : [String:Any] +} + +public struct RoomTyping { + public let roomID : String + public let user : MemberModel + public let typing : Bool +} + +enum SyncEventTopic : String { + case noActionTopic = "" + case deletedMessage = "deleted_message" + case clearRoom = "clear_room" + case delivered = "delivered" + case read = "read" + case sent = "sent" +} + +struct SyncEvent { + let id : String + let timestamp : Int64 + let actionTopic : SyncEventTopic + let payload : [String:Any] + + init(json: JSON) { + let id = json["id"].int64 ?? 0 + self.id = "\(id)" + self.timestamp = json["timestamp"].int64 ?? 0 + self.actionTopic = SyncEventTopic(rawValue: json["action_topic"].string ?? "") ?? .noActionTopic + self.payload = json["payload"].dictionaryObject ?? [:] + } +} + +extension SyncEvent { + func getDeletedMessageUniqId() -> [String] { + var result : [String] = [String]() + guard let data = payload["data"] as? [String:Any] else { + return result + } + guard let messages = data["deleted_messages"] as? [[String:Any]] else { + return result + } + + messages.forEach { (message) in + if let _message = message["message_unique_ids"] as? [String] { + _message.forEach({ (id) in + result.append(id) + }) + } + } + + return result + } + + func getClearRoomUniqId() -> [String] { + var result : [String] = [String]() + guard let data = payload["data"] as? [String:Any] else { + return result + } + guard let rooms = data["deleted_rooms"] as? [[String:Any]] else { + return result + } + + rooms.forEach { (room) in + if let id = room["unique_id"] as? String { + result.append(id) + } + } + + return result + } + + func updatetStatusMessage(){ + guard let data = payload["data"] as? [String:Any] else { + return + } + let jsonPayload = JSON(arrayLiteral: data)[0] + let commentId = jsonPayload["comment_id"].stringValue + let email = jsonPayload["email"].stringValue +// guard let commentDB = QiscusCoreLite.database.comment.find(id: commentId) else { +// return +// } +// if actionTopic == .delivered { +// RealtimeManager.shared.didReceiveMessageStatus(roomId: commentDB.roomId, commentId: commentDB.id, commentUniqueId: commentDB.uniqId, Status: .delivered, userEmail: email) +// }else if actionTopic == .read { +// RealtimeManager.shared.didReceiveMessageStatus(roomId: commentDB.roomId, commentId: commentDB.id, commentUniqueId: commentDB.uniqId, Status: .read, userEmail: email) +// } + } +} diff --git a/Source/QiscusCoreApi/Model/RoomModel.swift b/Source/QiscusCoreApi/Model/RoomModel.swift new file mode 100644 index 0000000..fbb8fe7 --- /dev/null +++ b/Source/QiscusCoreApi/Model/RoomModel.swift @@ -0,0 +1,105 @@ +// +// RoomListModel.swift +// QiscusCoreLite +// +// Created by Rahardyan Bisma on 26/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// +import Foundation +import SwiftyJSON + +public enum RoomType: String { + case single = "single" + case group = "group" + case channel = "public_channel" + + static let all = [single,group,channel] +} + +public class Meta { + public let currentPage : Int? + public let totalRoom : Int? + + init(json: JSON) { + self.currentPage = json["current_page"].int ?? json["total_page"].intValue + self.totalRoom = json["total_room"].int ?? json["total_data"].intValue + } + +} + +public class MetaRoomParticipant { + public let currentPage : Int? + public let perPage : Int? + public let total : Int? + + init(json: JSON) { + self.currentPage = json["current_page"].int ?? 0 + self.perPage = json["per_page"].int ?? 0 + self.total = json["total"].int ?? 0 + } + +} + +open class RoomModel { + public var onChange : (CommentModel) -> Void = { _ in} // data binding + public internal(set) var id : String = "" + public internal(set) var name : String = "" + public internal(set) var uniqueId : String = "" + public internal(set) var avatarUrl : URL? = nil + public internal(set) var type : RoomType = .group + public internal(set) var options : String? = nil + // can be update after got new comment + public internal(set) var lastComment : CommentModel? = nil + public internal(set) var participants : [MemberModel]? = nil + public internal(set) var unreadCount : Int = -1 + + init() {} + + init(json: JSON) { + self.id = json["id_str"].stringValue + self.name = json["room_name"].stringValue + self.uniqueId = json["unique_id"].stringValue + self.avatarUrl = json["avatar_url"].url ?? nil + self.options = json["options"].string ?? nil + self.unreadCount = json["unread_count"].intValue + let _lastComment = json["last_comment"] + + self.lastComment = CommentModel(json: _lastComment) + if let _participants = json["participants"].array { + var data = [MemberModel]() + for i in _participants { + data.append(MemberModel(json: i)) + } + self.participants = data + } + if let _type = json["chat_type"].string { + if _type == "group"{ + let _is_public_channel = json["is_public_channel"].bool ?? false + + if _is_public_channel == true { + self.type = .channel + }else{ + self.type = .group + } + + }else{ + self.type = .single + } + } + } + + enum CodingKeys: String, CodingKey { + case id = "id" + case idstr = "id_str" + case name = "room_name" + case uniqueId = "unique_id" + case avatarUrl = "avatar_url" + case chatType = "chat_type" + case options = "options" + + case unreadCount = "unread_count" + } + +} + + diff --git a/Source/QiscusCoreApi/Model/UserModel.swift b/Source/QiscusCoreApi/Model/UserModel.swift new file mode 100644 index 0000000..3a7107e --- /dev/null +++ b/Source/QiscusCoreApi/Model/UserModel.swift @@ -0,0 +1,117 @@ +// +// Auth.swift +// QiscusCoreLite +// +// Created by Qiscus on 19/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +import SwiftyJSON +import Foundation + +public enum SortType: String { + case asc = "asc" + case desc = "desc" +} + +public struct UserModel { + public var avatarUrl : URL = URL(string: "http://")! + public var email : String = "" + public var id : String = "" + public var rtKey : String = "" + public var token : String = "" + public var username : String = "" + public var extras : String = "" + + init() { } + + init(json: JSON) { + avatarUrl = json["avatar_url"].url ?? URL(string: "http://")! + email = json["email"].stringValue + id = json["id_str"].stringValue + rtKey = json["rtKey"].stringValue + token = json["token"].stringValue + username = json["username"].stringValue + extras = json["extras"].rawString() ?? "" + } +} + +extension UserModel { + private func filename(id: String, name: String) -> String { + return "\(id.reversed())_\(name)" + } + + internal func save(appID: String) { + // save in file + let defaults = UserDefaults.standard + defaults.set(self.id, forKey: filename(id: appID, name: "id")) + defaults.set(self.username, forKey: filename(id: appID, name: "username")) + defaults.set(self.email, forKey: filename(id: appID, name: "email")) + defaults.set(self.token, forKey: filename(id: appID, name: "token")) + defaults.set(self.rtKey, forKey: filename(id: appID, name: "rtKey")) + defaults.set(self.avatarUrl, forKey: filename(id: appID, name: "avatarUrl")) + defaults.set(self.extras, forKey: filename(id: appID, name: "extras")) + } + + mutating func loadUserProfile(appID: String) { + // save in cache + let storage = UserDefaults.standard + self.token = storage.string(forKey: filename(id: appID, name: "token")) ?? "" + self.id = storage.string(forKey: filename(id: appID, name: "id")) ?? "" + self.email = storage.string(forKey: filename(id: appID, name: "email")) ?? "" + self.username = storage.string(forKey: filename(id: appID, name: "username")) ?? "" + self.extras = storage.string(forKey: filename(id: appID, name: "extras")) ?? "" + self.avatarUrl = storage.url(forKey: filename(id: appID, name: "avatarUrl")) ?? URL(string: "http://")! + } + + internal func clear(appID: String) { + // remove file user + let storage = UserDefaults.standard + storage.removeObject(forKey: filename(id: appID, name: "id")) + storage.removeObject(forKey: filename(id: appID, name: "token")) + storage.removeObject(forKey: filename(id: appID, name: "username")) + storage.removeObject(forKey: filename(id: appID, name: "email")) + storage.removeObject(forKey: filename(id: appID, name: "rtKey")) + storage.removeObject(forKey: filename(id: appID, name: "avatarUrl")) + storage.removeObject(forKey: filename(id: appID, name: "syncEventId")) + storage.removeObject(forKey: filename(id: appID, name: "syncId")) + storage.removeObject(forKey: filename(id: appID, name: "isConnectedMQTT")) + storage.removeObject(forKey: filename(id: appID, name: "extras")) + storage.removeObject(forKey: filename(id: appID, name: "customHeader")) + storage.removeObject(forKey: filename(id: appID, name: "deviceToken")) + } +} + +open class MemberModel { + public var avatarUrl : URL? = nil + public var email : String = "" + public var id : String = "" + public var lastCommentReadId : Int = -1 + public var lastCommentReceivedId : Int = -1 + public var username : String = "" + private let userKey = "CoreMemKey_" + + init() { } + + init(json: JSON) { + self.id = json["email"].stringValue + self.username = json["username"].stringValue + self.avatarUrl = json["avatar_url"].url ?? nil + self.email = json["email"].stringValue + self.lastCommentReadId = json["last_comment_read_id"].intValue + self.lastCommentReceivedId = json["last_comment_received_id"].intValue + } +} + +extension MemberModel { + internal func saveLastOnline(_ time: Date) { + let db = UserDefaults.standard + db.set(time, forKey: self.userKey + "lastSeen") + } + + func lastSeen() -> Date? { + let db = UserDefaults.standard + return db.object(forKey: self.userKey + "lastSeen") as? Date + // MARK: TODO get alternative when null + } +} diff --git a/Source/QiscusCoreApi/Network/Encoding/ParameterEncoder.swift b/Source/QiscusCoreApi/Network/Encoding/ParameterEncoder.swift new file mode 100644 index 0000000..ce1763f --- /dev/null +++ b/Source/QiscusCoreApi/Network/Encoding/ParameterEncoder.swift @@ -0,0 +1,161 @@ +// +// URLParameterEncoder.swift +// QiscusCoreLite +// +// Created by Qiscus on 18/07/18. +// + +#if os(iOS) +import UIKit +#endif +import Foundation + +struct URLParameterEncoder: ParameterEncoder { + func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws { + + guard let url = urlRequest.url else { throw NetworkError.missingURL } + + if var urlComponents = URLComponents(url: url, + resolvingAgainstBaseURL: false), !parameters.isEmpty { + + urlComponents.queryItems = [URLQueryItem]() + + for (key,value) in parameters { + let queryItem = URLQueryItem(name: key, + value: "\(value)".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)) + urlComponents.queryItems?.append(queryItem) + } + urlRequest.url = urlComponents.url + } + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") + } + + } +} + +struct FormUrlEncode: FormUrlEncoder { + func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws { + + guard let url = urlRequest.url else { throw NetworkError.missingURL } + + if var urlComponents = URLComponents(url: url, + resolvingAgainstBaseURL: false), !parameters.isEmpty { + + urlComponents.queryItems = [URLQueryItem]() + + for (key,value) in parameters { + + if let dictionary = value as? [String: Any] { + do { + let jsonAsData = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) + let queryItem = URLQueryItem(name: key, value: jsonAsData.toJsonString()) + urlComponents.queryItems?.append(queryItem) + + }catch { + throw NetworkError.encodingFailed + } + } else if let array = value as? [Any] { + for value in array { + let queryItem = URLQueryItem(name: "\(key)[]", + value: "\(value)") + urlComponents.queryItems?.append(queryItem) + } + } else { + let queryItem = URLQueryItem(name: key, + value: "\(value)") + urlComponents.queryItems?.append(queryItem) + } + } + + if let queryData = urlComponents.query{ + let data : Data = queryData.data(using: .utf8)! + urlRequest.httpBody = data + } + + } + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") + + } + + } +} + +struct JSONParameterEncoder: ParameterEncoder { + func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws { + do { + let jsonAsData = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) + urlRequest.httpBody = jsonAsData + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + } + }catch { + throw NetworkError.encodingFailed + } + } + func urlEncode(urlRequest: inout URLRequest, with parameters: Parameters) throws { + + guard let url = urlRequest.url else { throw NetworkError.missingURL } + + if var urlComponents = URLComponents(url: url, + resolvingAgainstBaseURL: false), !parameters.isEmpty { + + urlComponents.queryItems = [URLQueryItem]() + + for (key,value) in parameters { + let queryItem = URLQueryItem(name: key, + value: "\(value)") + urlComponents.queryItems?.append(queryItem) + } + urlRequest.url = urlComponents.url + } + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + } + + } +} + +struct URLParameterNotEncode: ParameterNotEncoder { + func notEncode(urlRequest: inout URLRequest, with parameters: Parameters) throws { + + guard let url = urlRequest.url else { throw NetworkError.missingURL } + + if var urlComponents = URLComponents(url: url, + resolvingAgainstBaseURL: false), !parameters.isEmpty { + + urlComponents.queryItems = [URLQueryItem]() + + for (key,value) in parameters { + let queryItem = URLQueryItem(name: key, + value: "\(value)") + urlComponents.queryItems?.append(queryItem) + } + urlRequest.url = urlComponents.url + } + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type") + } + + } +} + +//extension String { +// +// var stripped: String { +// let okayChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-=().!_") +// return self.filter {okayChars.contains($0) } +// } +//} + +extension NSNumber { + fileprivate var isBool: Bool { + return CFBooleanGetTypeID() == CFGetTypeID(self) + + } +} diff --git a/Source/QiscusCoreApi/Network/Encoding/ParameterEncoding.swift b/Source/QiscusCoreApi/Network/Encoding/ParameterEncoding.swift new file mode 100644 index 0000000..df79ae4 --- /dev/null +++ b/Source/QiscusCoreApi/Network/Encoding/ParameterEncoding.swift @@ -0,0 +1,62 @@ +// +// ParameterEncoding.swift +// QiscusCoreLite +// +// Created by Qiscus on 18/07/18. +// + +import Foundation + +public typealias Parameters = [String:Any] + +public protocol ParameterEncoder { + func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws +} + +public protocol FormUrlEncoder { + func encode(urlRequest: inout URLRequest, with parameters: Parameters) throws +} + +public protocol ParameterNotEncoder{ + func notEncode(urlRequest: inout URLRequest, with parameters: Parameters) throws +} + +public enum ParameterEncoding { + + case urlEncoding + case jsonEncoding + case jsonUrlEncoding + case formUrlEncode + + public func encode(urlRequest: inout URLRequest, + bodyParameters: Parameters?, + urlParameters: Parameters?) throws { + do { + switch self { + case .formUrlEncode: + guard let urlParameters = urlParameters else { return } + try FormUrlEncode().encode(urlRequest: &urlRequest, with: urlParameters) + case .urlEncoding: + guard let urlParameters = urlParameters else { return } + try URLParameterEncoder().encode(urlRequest: &urlRequest, with: urlParameters) + case .jsonEncoding: + guard let bodyParameters = bodyParameters else { return } + try JSONParameterEncoder().encode(urlRequest: &urlRequest, with: bodyParameters) + case .jsonUrlEncoding: + //Using method get and using query param + guard let urlParameters = urlParameters else { return } + try JSONParameterEncoder().urlEncode(urlRequest: &urlRequest, with: urlParameters) + + } + }catch { + throw error + } + } +} + + +public enum NetworkError : String, Error { + case parametersNil = "Parameters were nil." + case encodingFailed = "Parameter encoding failed." + case missingURL = "URL is nil." +} diff --git a/Source/QiscusCoreApi/Network/NetworkManager.swift b/Source/QiscusCoreApi/Network/NetworkManager.swift new file mode 100644 index 0000000..5559ba1 --- /dev/null +++ b/Source/QiscusCoreApi/Network/NetworkManager.swift @@ -0,0 +1,570 @@ +// +// NetworkManager.swift +// QiscusCoreLite +// +// Created by Qiscus on 18/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +#if os(iOS) +import UIKit +#endif +import Foundation + +enum NetworkResponse:String { + case success + case clientError = "Client Error." + case serverError = "Server Error." + case badRequest = "Bad request" + case outdated = "The url you requested is outdated." + case failed = "Network request failed." + case noData = "Response returned with no data to decode." + case unableToDecode = "Response not JSON or undefined." +} + +enum Result{ + case success + case failure(String) +} + +enum NetworkEnvironment : String { + case production + case staging +} + +class NetworkManager { + static let environment : NetworkEnvironment = .production + let clientRouter : Router + let roomRouter : Router + let commentRouter : Router + let userRouter : Router + + init(core: QiscusCoreAPI) { + self.clientRouter = Router(core: core) + self.roomRouter = Router(core: core) + self.commentRouter = Router(core: core) + self.userRouter = Router(core: core) + } + + func handleNetworkResponse(_ response: HTTPURLResponse) -> Result{ + QiscusLogger.debugPrint("response code \(response.statusCode)") + switch response.statusCode { + case 200...299: return .success + case 400...499: return .failure(NetworkResponse.clientError.rawValue) + case 500...599: return .failure(NetworkResponse.serverError.rawValue) + case 600: return .failure(NetworkResponse.outdated.rawValue) + default: return .failure(NetworkResponse.failed.rawValue) + } + } + +} +// MARK: Client +extension NetworkManager { + /// get nonce for JWT authentication + /// + /// - Parameter completion: @ecaping on getNonce request done return Optional(QNonce) and Optional(Error message) + func getNonce(onSuccess: @escaping (QNonce) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.nonce) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let nonce = QNonce(json: response) + onSuccess(nonce) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + onError(QError(message: errorMessage)) + } + } + } + } + + + /// login + /// + /// - Parameters: + /// - identityToken: identity token from your server after verify the nonce + /// - completion: @escaping when success login retrun Optional(UserModel) and Optional(String error message) + func login(identityToken: String, onSuccess: @escaping (UserModel) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.loginRegisterJWT(identityToken: identityToken)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let user = UserApiResponse.user(from: response) + onSuccess(user) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error identityToken Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + /// login + /// + /// - Parameters: + /// - email: username or email identifier + /// - password: user password to login to qiscus sdk + /// - username: user display name + /// - avatarUrl: user avatar url + /// - completion: @escaping on + func login(email: String, password: String ,username : String? ,avatarUrl : String?, extras: [String:Any]?, onSuccess: @escaping (UserModel) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.loginRegister(user: email, password: password,username: username,avatarUrl: avatarUrl, extras: extras)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let user = UserApiResponse.user(from: response) + onSuccess(user) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + onError(QError(message:errorMessage)) + } + } + } + } + + + /// register device token for notification + /// + /// - Parameters: + /// - deviceToken: string device token for push notification + /// - isDevelopment : default is false / using production + /// - completion: @escaping when success register device token to sdk server returning value bool(success or not) and Optional String(error message) + func registerDeviceToken(deviceToken: String, isDevelopment: Bool = false, onSuccess: @escaping (Bool) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.registerDeviceToken(token: deviceToken, isDevelopment: isDevelopment)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let _ = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + onSuccess(true) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + onError(QError(message: errorMessage)) + } + } + } + } + + /// remove device token for notification + /// + /// - Parameters: + /// - deviceToken: string device token to be remove from server + /// - isDevelopment : default is false / using production + /// - completion: @escaping when success remove device token to sdk server returning value bool(success or not) and Optional String(error message) + func removeDeviceToken(deviceToken: String, isDevelopment:Bool = false, onSuccess: @escaping (Bool) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.removeDeviceToken(token: deviceToken, isDevelopment : isDevelopment)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let _ = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + onSuccess(true) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + onError(QError(message: errorMessage)) + } + } + } + } + + + /// get user profile + /// + /// - Parameter completion: @escaping when success get user profile, return Optional(UserModel) and Optional(String error) + func getProfile(onSuccess: @escaping (UserModel) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.myProfile) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message:NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let user = UserApiResponse.user(from: response) + onSuccess(user) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error syncEvent Code =\(response.statusCode)\(errorMessage)") + onError(QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + /// update user profile + /// + /// - Parameters: + /// - displayName: user new displayname + /// - avatarUrl: user new avatar url + /// - completion: @escaping when finish updating user profile return update Optional(UserModel) and Optional(String error message) + func updateProfile(displayName: String = "", avatarUrl: URL? = nil, extras: [String : Any]? = nil, onSuccess: @escaping (UserModel) -> Void, onError: @escaping (QError) -> Void) { + if displayName.isEmpty && avatarUrl == nil { + onError(QError(message: "Please set display name")) + return + } + clientRouter.request(.updateMyProfile(name: displayName, avatarUrl: avatarUrl?.absoluteString, extras: extras)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let user = UserApiResponse.user(from: response) + onSuccess(user) +// }else { +// onError(QError(message: "Failed to parsing results")) +// } + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error updateProfile Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + @available(*, deprecated, message: "will soon become unavailable.") + func syncEvent(lastId: String, onSuccess: @escaping ([SyncEvent]) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.syncEvent(startEventId: lastId)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { onError(QError(message: "Failed to parsing response.")); return} + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(syncEvent: responseData) + onSuccess(response) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error syncEvent Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + func synchronizeEvent(lastEventId: String, onSuccess: @escaping ([SyncEvent]) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.syncEvent(startEventId: lastEventId)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { onError(QError(message: "Failed to parsing response.")); return} + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(syncEvent: responseData) + onSuccess(response) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error syncEvent Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + func sync(lastCommentReceivedId: String, completion: @escaping ([CommentModel]?, String?) -> Void) { + clientRouter.request(.sync(lastReceivedCommentId: lastCommentReceivedId)) { (data, response, error) in + if error != nil { + completion(nil, error?.localizedDescription ?? "Please check your network connection.") + } + if data == nil { completion(nil, "Failed to parsing response."); return } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let comments = CommentApiResponse.comments(from: response) + completion(comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, errorMessage) + } + } + } + } + + func blockUser(email: String, onSuccess: @escaping (MemberModel) -> Void, onError: @escaping (QError) -> Void) { + userRouter.request(.block(email: email)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { onError(QError(message: "Failed to parsing response.")); return} + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let member = UserApiResponse.blockUser(from: response) + onSuccess(member) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error blockUser Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + func unblockUser(email: String, onSuccess: @escaping (MemberModel) -> Void, onError: @escaping (QError) -> Void) { + userRouter.request(.unblock(email: email)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { + onError(QError(message: "Failed to parsing response.")) + return + + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let member = UserApiResponse.blockUser(from: response) + onSuccess(member) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error unblockUser Code =\(response.statusCode)\(errorMessage)") + onError(QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + func getBlokedUser(page: Int?, limit: Int?, onSuccess: @escaping ([MemberModel]) -> Void, onError: @escaping (QError) -> Void) { + userRouter.request(.listBloked(page: page, limit: limit)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { onError(QError(message: "Failed to parsing response.")); return} + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + if let members = UserApiResponse.blockedUsers(from: response) { + onSuccess(members) + }else { + onError(QError(message: "blocked_users: [], total :0")) + } + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error getBlockUser Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + func getUsers(limit : Int?, page: Int?, querySearch: String?, onSuccess: @escaping ([MemberModel], Meta) -> Void, onError: @escaping (QError) -> Void) { + userRouter.request(.getUsers(page: page, limit: limit, querySearch: querySearch)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { onError(QError(message: "Failed to parsing response.")); return} + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let meta = UserApiResponse.meta(from: response) + if let members = UserApiResponse.allUser(from: response) { + onSuccess(members, meta) + }else { + onError(QError(message: "Result failed to parse")) + } + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error getUsers Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + + func event_report(moduleName: String, event: String, message: String, onSuccess: @escaping (Bool) -> Void, onError: @escaping (QError) -> Void) { + clientRouter.request(.eventReport(moduleName: moduleName, event: event, message: message)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if data == nil { onError(QError(message: "Failed to parsing response.")); return} + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + onSuccess(true) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + onError(QError(message: "json: \(jsondata)")) + } catch { + QiscusLogger.errorPrint("Error event_report Code =\(response.statusCode)\(errorMessage)") + onError( QError(message: NetworkResponse.unableToDecode.rawValue)) + } + } + } + } + } + +} diff --git a/Source/QiscusCoreApi/Network/NetworkMessage.swift b/Source/QiscusCoreApi/Network/NetworkMessage.swift new file mode 100644 index 0000000..9079190 --- /dev/null +++ b/Source/QiscusCoreApi/Network/NetworkMessage.swift @@ -0,0 +1,329 @@ +// +// NetworkMessage.swift +// QiscusCoreLite +// +// Created by Qiscus on 14/08/18. +// + +import Foundation + +// MARK: Comment +extension NetworkManager { + + /// load comments on a room or channel + /// + /// - Parameters: + /// - roomId: room id or unique id + /// - lastCommentId: last recieved comment id before loadmore + /// - after: if true returns comments with id >= last_comment_id. if false and last_comment_id is specified, returns last 20 comments with id < last_comment_id. if false and last_comment_id is not specified, returns last 20 comments + /// - limit: limit for the result default value is 20, max value is 100 + /// - completion: @escaping when success load comments, return Optional([CommentModel]) and Optional(String error message) + func loadComments(roomId: String, lastCommentId: Int, after: Bool? = nil, limit: Int? = 20, completion: @escaping ([CommentModel]?, QError?) -> Void) { + commentRouter.request(.loadComment(topicId: roomId, lastCommentId: lastCommentId, after: after, limit: limit)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let comments = CommentApiResponse.comments(from: response) + completion(comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + completion(nil, QError(message: errorMessage)) + } + } + } + } + + + /// post comment + /// + /// - Parameters: + /// - roomId: chat room id + /// - type: comment type + /// - comment: comment text (required when type == text) + /// - payload: comment payload (string on json format) + /// - extras: comment extras (string on json format) + /// - uniqueTempId: - + /// - completion: @escaping when success post comment, return Optional(CommentModel) and Optional(String error message) + func postComment(roomId: String, type: String = "text", message: String, payload: [String:Any]? = nil, extras: [String:Any]? = nil, uniqueTempId: String = "", completion: @escaping(CommentModel?, String?) -> Void) { + commentRouter.request(.postComment(topicId: roomId, type: type, message: message, payload: payload, extras: extras, uniqueTempId: uniqueTempId)) { (data, response, error) in + if error != nil { + completion(nil, error?.localizedDescription ?? "Please check your network connection.") + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let comment = CommentApiResponse.comment(from: response) + completion(comment, nil) + case .failure(let errorMessage): + if data != nil { + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + completion(nil, "json: \(jsondata)") + } catch { + QiscusLogger.errorPrint("Error postComment Code =\(response.statusCode)\(errorMessage)") + completion(nil, NetworkResponse.unableToDecode.rawValue) + } + }else{ + QiscusLogger.errorPrint("Error postComment Code =\(response.statusCode)\(errorMessage)") + completion(nil, NetworkResponse.unableToDecode.rawValue) + } + } + } + } + } + + + /// delete comments + /// + /// - Parameters: + /// - commentUniqueId: comment unique id or you can use comment.uniqueTempId + /// - completion: @escaping when success delete comments, return deleted comment Optional([CommentModel]) and Optional(String error message) + func deleteComment(commentUniqueId: [String], completion: @escaping ([CommentModel]?, QError?) -> Void) { + commentRouter.request(.delete(commentUniqueId: commentUniqueId)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let comments = CommentApiResponse.comments(from: response) + completion(comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, QError(message: errorMessage)) + } + } + } + } + + // todo: add more documentation + func updateCommentStatus(roomId: String, lastCommentReadId: String? = nil, lastCommentReceivedId: String? = nil) { + commentRouter.request(.updateStatus(roomId: roomId, lastCommentReadId: lastCommentReadId, lastCommentReceivedId: lastCommentReceivedId)) { (data, response, error) in + + var commentReceivedId = "0" + var commentReadId = "0" + + if let readID = lastCommentReadId { + commentReadId = readID + } + + if let receivedID = lastCommentReceivedId { + commentReceivedId = receivedID + } + + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + return + case .failure(let errorMessage): + do { + if let data = data{ + let jsondata = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + self.event_report(moduleName: "API", event: "update_comment_status", message: "commentReceivedId=\(commentReceivedId), commentReadId=\(commentReadId), error: \(jsondata)", onSuccess: { (success) in + + }) { (error) in + QiscusLogger.debugPrint(error.message) + } + }else{ + + QiscusLogger.errorPrint("Error updateCommentStatus Code =\(response.statusCode), \(errorMessage)") + } + + } catch { + var message = errorMessage + if error != nil { + message = error.localizedDescription + } + + QiscusLogger.errorPrint("Error updateCommentStatus Code =\(response.statusCode), \(message)") + + self.event_report(moduleName: "API", event: "update_comment_status", message: "commentReceivedId=\(commentReceivedId), commentReadId=\(commentReadId), Code =\(response.statusCode), error \(error.localizedDescription)", onSuccess: { (success) in + + }) { (error) in + QiscusLogger.debugPrint(error.message) + } + + } + } + } + + } + } + + /// Get total unread message + /// + /// - Parameter completion: result as Int + func unreadCount(completion: @escaping(Int, QError?) -> Void) { + userRouter.request(.unread) { (data, response, error) in + if error != nil { + completion(0, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(0, QError(message: NetworkResponse.noData.rawValue)) + return + } + let unread = ApiResponse.decode(unread: responseData) + completion(unread,nil) + case .failure(let errorMessage): + completion(0, QError(message: "Can't parse error, when request unread count.")) + } + } + } + } + + /// Search message from server + /// + /// - Parameters: + /// - keyword: required, keyword to search + /// - roomID: optional, search on specific room by room id + /// - lastCommentId: optional, will get comments aafter this id + func searchMessage(keyword: String, roomID: String?, lastCommentId: Int?, completion: @escaping ([CommentModel]?, QError?) -> Void) { + commentRouter.request(.search(keyword: keyword, roomID: roomID, lastCommentID: lastCommentId)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let comments = CommentApiResponse.comments(from: response) + completion(comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + completion(nil, QError(message: errorMessage)) + } + } + } + } + + /// Clear message from + /// + /// - Parameters: + /// - roomsUniqueID: room unique id where you want to clear + /// - completion: got error if exist + func clearMessage(roomsUniqueID: [String], completion: @escaping (QError?) -> Void) { + if roomsUniqueID.isEmpty { + completion(QError.init(message: "Parameter can't be empty")) + } + commentRouter.request(.clear(roomChannelIds: roomsUniqueID)) { (data, response, error) in + if error != nil { + completion(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + completion(nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + completion(QError(message: errorMessage)) + } + } + } + } + + /// readReceiptStatus + /// + /// - Parameters: + /// - commentId: comment id + /// - completion: commentInfo + func readReceiptStatus(commentId id: String, completion: @escaping (CommentInfo?, QError?) -> Void) { + commentRouter.request(.statusComment(id: id)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let comment = CommentApiResponse.comment(from: response) + + + var commentInfo = CommentInfo() + commentInfo.comment = comment + + if let commentDeliveredUser = CommentApiResponse.commentDeliveredUser(from: response){ + commentInfo.deliveredUser = commentDeliveredUser + } + if let commentPendingUser = CommentApiResponse.commentPendingUser(from: response){ + commentInfo.sentUser = commentPendingUser + } + if let commentReadUser = CommentApiResponse.commentReadUser(from: response){ + commentInfo.readUser = commentReadUser + } + + completion(commentInfo, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, QError(message: errorMessage)) + } + } + } + + } +} diff --git a/Source/QiscusCoreApi/Network/NetworkRoom.swift b/Source/QiscusCoreApi/Network/NetworkRoom.swift new file mode 100644 index 0000000..ffd504d --- /dev/null +++ b/Source/QiscusCoreApi/Network/NetworkRoom.swift @@ -0,0 +1,509 @@ +// +// NetworkRoom.swift +// QiscusCoreLite +// +// Created by Qiscus on 14/08/18. +// + +import Foundation + +// MARK: Room +extension NetworkManager { + /// get room chat room list + /// + /// - Parameters: + /// - showParticipant: Bool (true = include participants obj to the room, false = participants obj nil) + /// - limit: limit room per page + /// - page: page + /// - roomType: (single, group, public_channel) by default returning all type + /// - showRemoved: Bool (true = include room that has been removed, false = exclude room that has been removed) + /// - showEmpty: Bool (true = it will show all rooms that have been created event there are no messages, default is false where only room that have at least one message will be shown) + /// - completion: @escaping when success get room list returning Optional([RoomModel]), Optional(Meta) contain page, total_room per page, Optional(String error message) + func getRoomList(showParticipant: Bool = true, limit: Int? = 20, page: Int? = 1, roomType: RoomType? = nil, showRemoved: Bool = false, showEmpty: Bool = false, completion: @escaping([RoomModel]?, Meta?, String?) -> Void) { + roomRouter.request(.roomList(showParticipants: showParticipant, limit: limit, page: page, roomType: roomType, showRemoved: showRemoved, showEmpty: showEmpty)) { (data, response, error) in + if error != nil { + completion(nil, nil, error?.localizedDescription ?? "Please check your network connection.") + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let rooms = RoomApiResponse.rooms(from: response) + let meta = RoomApiResponse.meta(from: response) + completion(rooms, meta, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, nil, errorMessage) + } + } + } + } + + + /// get room by roomIds | uniqueIds + /// + /// - Parameters: + /// - roomIds: array of room id + /// - roomUniqueIds: array of room unique id + /// - showRemoved: Bool (true = include room that has been removed, false = exclude room that has been removed) + /// - showEmpty: Bool (true = it will show all rooms that have been created event there are no messages, default is false where only room that have at least one message will be shown) + /// - completion: @escaping when success get room list returning Optional([RoomModel]), Optional(Meta) contain page, total_room per page, Optional(String error message) + func getRoomInfo(roomIds: [String]? = [], roomUniqueIds: [String]? = [], showParticipant: Bool = false, showRemoved: Bool = false, completion: @escaping ([RoomModel]?, QError?) -> Void) { + roomRouter.request(.roomInfo(roomId: roomIds, roomUniqueId: roomUniqueIds, showParticipants: showParticipant, showRemoved: showRemoved)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let rooms = RoomApiResponse.rooms(from: response) + completion(rooms, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, QError(message: errorMessage)) + } + } + } + } + + + /// create group room + /// + /// - Parameters: + /// - name: room name + /// - participants: array of participant's sdk email + /// - avatarUrl: room avatar url + /// - options : JsonString + /// - completion: @escaping when success create room, return created Optional(RoomModel), Optional(String error message) + func createRoom(name: String, participants: [String], avatarUrl: URL? = nil, options: String? = nil, completion: @escaping (RoomModel?, String?) -> Void) { + roomRouter.request(.createNewRoom(name: name, participants: participants, avatarUrl: avatarUrl, options: options)) { (data, response, error) in + if error != nil { + completion(nil, error?.localizedDescription ?? "Please check your network connection.") + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let rooms = RoomApiResponse.room(from: response) + completion(rooms, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, errorMessage) + } + } + } + } + + /// update existing room + /// + /// - Parameters: + /// - roomId: room id + /// - roomName: new room name + /// - avatarUrl: new room avatar + /// - options: new room options + /// - completion: @escaping when success update room, return created Optional(RoomModel), Optional(String error message) + func updateRoom(roomId: String, roomName: String?, avatarUrl: URL?, options: String?, completion: @escaping (RoomModel?, QError?) -> Void) { + roomRouter.request(.updateRoom(roomId: roomId, roomName: roomName, avatarUrl: avatarUrl, options: options)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + completion(room, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, QError(message: errorMessage)) + } + } + } + } + + + /// get room with target sdk email or create if not exist yet + /// + /// - Parameters: + /// - targetSdkEmail: user's target sdk email + /// - avatarUrl: room avatar url + /// - distincId: distinc id + /// - options: room options (string json) + /// - completion: @escaping when success update room, return created Optional(RoomModel), Optional([CommentModel]), Optional(String error message) + @available(*, deprecated, message: "will soon become unavailable.") + func getOrCreateRoomWithTarget(targetSdkEmail: String, options: String? = nil, onSuccess: @escaping (RoomModel,[CommentModel]?) -> Void, onError: @escaping (QError) -> Void) { + roomRouter.request(.roomWithTarget(email: [targetSdkEmail], options: options)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + let comments = CommentApiResponse.comments(from: response) + onSuccess(room,comments) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + onError(QError(message: errorMessage)) + } + } + } + } + + /// chatUser with userId + /// + /// - Parameters: + /// - userId: user's target sdk userId + /// - avatarUrl: room avatar url + /// - extras: room options (string json) + /// - completion: @escaping when success update room, return created Optional(RoomModel), Optional([CommentModel]), Optional(String error message) + func chatUser(userId: String, extras: String? = nil, onSuccess: @escaping (RoomModel,[CommentModel]?) -> Void, onError: @escaping (QError) -> Void) { + roomRouter.request(.roomWithTarget(email: [userId], options: extras)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + let comments = CommentApiResponse.comments(from: response) + onSuccess(room,comments) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + onError(QError(message: errorMessage)) + } + } + } + } + + + /// get room with channel type behavior + /// + /// - Parameters: + /// - uniqueId: channel uniqueId + /// - name: channel name (if not defined it will use unique id as default) + /// - avatarUrl: channel avatar + /// - options: channel options + /// - completion: @escaping when success get or create channel, return Optional(RoomModel), Optional([CommentModel]), Optional(String error) + func getOrCreateChannel(uniqueId: String, name: String? = nil, avatarUrl: URL? = nil, options: String? = nil, completion: @escaping (RoomModel?, [CommentModel]?, String?) -> Void) { + roomRouter.request(.channelWithUniqueId(uniqueId: uniqueId, name: name, avatarUrl: avatarUrl, options: options)) { (data, response, error) in + if error != nil { + completion(nil, nil, error?.localizedDescription ?? "Please check your network connection.") + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + let comments = CommentApiResponse.comments(from: response) + completion(room, comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, nil, errorMessage) + } + } + } + } + + /// create channel + /// + /// - Parameters: + /// - uniqueId: channel uniqueId + /// - name: channel name (if not defined it will use unique id as default) + /// - avatarURL: channel avatar + /// - extras: channel extras as jsonString + /// - completion: @escaping when success get or create channel, return Optional(RoomModel), Optional([CommentModel]), Optional(String error) + func createChannel(uniqueId: String, name: String? = nil, avatarURL: URL? = nil, extras: String? = nil, completion: @escaping (RoomModel?, [CommentModel]?, String?) -> Void) { + roomRouter.request(.channelWithUniqueId(uniqueId: uniqueId, name: name, avatarUrl: avatarURL, options: extras)) { (data, response, error) in + if error != nil { + completion(nil, nil, error?.localizedDescription ?? "Please check your network connection.") + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + let comments = CommentApiResponse.comments(from: response) + completion(room, comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, nil, errorMessage) + } + } + } + } + + /// get channel + /// + /// - Parameters: + /// - uniqueId: channel uniqueId + /// - completion: @escaping when success get or create channel, return Optional(RoomModel), Optional([CommentModel]), Optional(String error) + func getChannel(uniqueId: String, completion: @escaping (RoomModel?, [CommentModel]?, String?) -> Void) { + roomRouter.request(.channelWithUniqueId(uniqueId: uniqueId, name: nil, avatarUrl: nil, options: nil)) { (data, response, error) in + if error != nil { + completion(nil, nil, error?.localizedDescription ?? "Please check your network connection.") + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, nil, NetworkResponse.noData.rawValue) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + let comments = CommentApiResponse.comments(from: response) + completion(room, comments, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, nil, errorMessage) + } + } + } + } + + + /// get chat room by id + /// + /// - Parameters: + /// - roomId: room id + /// - completion: @escaping when success get room, return Optional(RoomModel), Optional([CommentModel]), Optional(String error message) + func getRoomById(roomId: String, onSuccess: @escaping (RoomModel, [CommentModel]?) -> Void, onError: @escaping (QError) -> Void) { + roomRouter.request(.getRoomById(roomId: roomId)) { (data, response, error) in + if error != nil { + onError(QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + onError(QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let room = RoomApiResponse.room(from: response) + let comments = CommentApiResponse.comments(from: response) + onSuccess(room,comments) + case .failure(let errorMessage): + if let data = data{ + do { + let jsondata = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + } + onError(QError(message: errorMessage)) + + } + } + } + } + + /// get participants to a chat room + /// + /// - Parameters: + /// - roomId: chat room id + /// - userSdkEmail: array of user's sdk email + /// - offset : default is 0 + /// - sorting : default is asc + /// - completion: @escaping when success add participant to room, return added participants Optional([MemberModel]), Optional(String error message) + func getParticipants(roomUniqeId id: String, page : Int? = 1, limit : Int? = 100, offset: Int? = 0, sorting: SortType? = nil, completion: @escaping ([MemberModel]?, MetaRoomParticipant?, QError?) -> Void) { + roomRouter.request(.getParticipant(roomId: id, page: page, limit : limit, offset: offset, sorting: sorting)) { (data, response, error) in + if error != nil { + completion(nil, nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + let response = ApiResponse.decode(from: responseData) + let members = RoomApiResponse.participants(from: response) + let meta = RoomApiResponse.metaRoomParticipant(from: response) + completion(members, meta, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(nil, nil, QError(message: errorMessage)) + } + } + } + } + + /// add participants to a chat room + /// + /// - Parameters: + /// - roomId: chat room id + /// - userSdkEmail: array of user's sdk email + /// - completion: @escaping when success add participant to room, return added participants Optional([MemberModel]), Optional(String error message) + func addParticipants(roomId: String, userSdkEmail: [String], completion: @escaping ([MemberModel]?, QError?) -> Void) { + roomRouter.request(.addParticipant(roomId: roomId, emails: userSdkEmail)) { (data, response, error) in + if error != nil { + completion(nil, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard let responseData = data else { + completion(nil, QError(message: NetworkResponse.noData.rawValue)) + return + } + + let response = ApiResponse.decode(from: responseData) + let members = RoomApiResponse.addParticipants(from: response) + completion(members, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + completion(nil, QError(message: errorMessage)) + } + } + } + } + + /// Remove + /// + /// - Parameters: + /// - roomId: room id where you want to remove member + /// - userSdkEmail: array if qiscus email + /// - completion: Response true if success and false if error, and error message if exist + func removeParticipants(roomId: String, userSdkEmail: [String], completion: @escaping(Bool, QError?) -> Void) { + roomRouter.request(.removeParticipant(roomId: roomId, emails: userSdkEmail)) { (data, response, error) in + if error != nil { + completion(false, QError(message: error?.localizedDescription ?? "Please check your network connection.")) + } + if let response = response as? HTTPURLResponse { + let result = self.handleNetworkResponse(response) + switch result { + case .success: + guard data != nil else { + completion(false, QError(message: NetworkResponse.noData.rawValue)) + return + } + + completion(true, nil) + case .failure(let errorMessage): + do { + let jsondata = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) + QiscusLogger.errorPrint("json: \(jsondata)") + } catch { + + } + + completion(false, QError(message: errorMessage)) + } + } + } + } +} diff --git a/Source/QiscusCoreApi/Network/Service/Endpoint.swift b/Source/QiscusCoreApi/Network/Service/Endpoint.swift new file mode 100644 index 0000000..b063ef3 --- /dev/null +++ b/Source/QiscusCoreApi/Network/Service/Endpoint.swift @@ -0,0 +1,229 @@ +// +// Endpoint.swift +// QiscusCoreLite +// +// Created by Qiscus on 17/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +import Foundation + +protocol EndPoint { + var path : String { get } + var httpMethod : HTTPMethod { get } + var task : HTTPTask { get } +} + +//var AUTHTOKEN : String { +// get { +// if let user = QiscusCoreLite.config.user { +// return user.token +// }else { +// return "" +// } +// +// } +//} +// +//var BASEURL : URL { +// get { +// if let server = ConfigManager.shared.server { +// return server.url +// }else { +// return URL.init(string: "https://api.qiscus.com/api/v2/mobile")! +// } +// } +//} +// +//var HEADERS : [String: String] { +// get { +// var headers = [ +// "QISCUS-SDK-PLATFORM": "iOS", +// "QISCUS-SDK-DEVICE-BRAND": "Apple", +// "QISCUS-SDK-VERSION": VERSION_NUMBER, +// "QISCUS-SDK-DEVICE-MODEL" : UIDevice.modelName, +// "QISCUS-SDK-DEVICE-OS-VERSION" : UIDevice.current.systemVersion +// ] +// if let appID = ConfigManager.shared.appID { +// headers["QISCUS-SDK-APP-ID"] = appID +// } +// +// if let user = ConfigManager.shared.user { +// if let appid = ConfigManager.shared.appID { +// headers["QISCUS-SDK-APP-ID"] = appid +// } +// if !user.token.isEmpty { +// headers["QISCUS-SDK-TOKEN"] = user.token +// } +// if !user.email.isEmpty { +// headers["QISCUS-SDK-USER-ID"] = user.email +// } +// } +// +// if let customHeader = ConfigManager.shared.customHeader { +// headers.merge(customHeader as! [String : String]){(_, new) in new} +// } +// +// return headers +// } +//} +///// + + +// MARK: General API +internal enum APIClient { + case sync(lastReceivedCommentId: String) + case syncEvent(startEventId : String) + case search(keyword: String, roomId: String?, lastCommentId: Int?) + case registerDeviceToken(token: String, isDevelopment: Bool) // + case removeDeviceToken(token: String, isDevelopment: Bool) // + case loginRegister(user: String, password: String , username: String?, avatarUrl: String?, extras: [String:Any]?) // + case loginRegisterJWT(identityToken: String) // + case nonce // + case unread + case myProfile // + case updateMyProfile(name: String?, avatarUrl: String?, extras: [String : Any]?) // + case upload + case eventReport(moduleName: String, event: String, message: String) +} + +extension APIClient : EndPoint { + var path: String { + switch self { + case .sync( _): + return "/sync" + case .syncEvent( _): + return "/sync_event" + case .search( _, _, _): + return "/search_messages" + case .registerDeviceToken( _, _): + return "/set_user_device_token" + case .removeDeviceToken( _, _): + return "/remove_user_device_token" + case .loginRegister( _, _, _, _, _): + return "/login_or_register" + case .loginRegisterJWT( _): + return "/auth/verify_identity_token" + case .nonce : + return "/auth/nonce" + case .unread: + return "/total_unread_count" + case .myProfile: + return "/my_profile" + case .updateMyProfile( _, _, _): + return "/my_profile" + case .upload: + return "/upload" + case .eventReport( _, _, _): + return "/event_report" + } + } + + var httpMethod: HTTPMethod { + switch self { + case .sync, .syncEvent, .unread, .myProfile: + return .get + case .search, .registerDeviceToken, .removeDeviceToken, .loginRegister, .loginRegisterJWT, .upload, .nonce, .eventReport: + return .post + case .updateMyProfile : + return .patch + } + } + + var task: HTTPTask { + switch self { + case .sync(let lastReceivedCommentId) : + let param = [ + "last_received_comment_id" : lastReceivedCommentId + ] as [String : Any] + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: param) + case .syncEvent(let startEventId): + let param = [ + "start_event_id" : startEventId + ] as [String : Any] + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: param) + case .search(let keyword,let roomId,let lastCommentId) : + var param = [ + "query" : keyword + ] as [String : Any] + + if let roomid = roomId { + param["room_id"] = roomid + } + + if let lastcommentid = lastCommentId { + param["last_comment_id"] = lastcommentid + } + + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .registerDeviceToken(let token, let isDevelopment): + let param = [ + "device_token" : token, + "device_platform" : "ios", + "is_development" : isDevelopment + ] as [String : Any] + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .removeDeviceToken(let token, let isDevelopment): + let param = [ + "device_token" : token, + "device_platform" : "ios", + "is_development" : isDevelopment + ] as [String : Any] + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .loginRegister(let user, let password, let username, let avatarUrl, let extras): + var param = [ + "email" : user, + "password" : password, + ] as [String : Any] + + if let usernm = username { + param["username"] = usernm + } + if let avatarurl = avatarUrl{ + param["avatar_url"] = avatarurl + } + if let extra = extras { + param["extras"] = extra + } + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .loginRegisterJWT(let identityToken): + let param = [ + "identity_token" : identityToken + ] + + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .nonce : + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .unread : + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: nil) + case .myProfile : + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: nil) + case .updateMyProfile(let name, let avatarUrl, let extras) : + var param = [String : Any]() + + if let newName = name { + param["name"] = newName + } + + if let newAvatarUrl = avatarUrl { + param["avatar_url"] = newAvatarUrl + } + + if let _extras = extras { + param["extras"] = _extras + } + + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .upload : + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonEncoding, urlParameters: nil) + + case .eventReport(let moduleName, let event, let message) : + let param = [ + "module_name" : moduleName, + "event" : event, + "message" : message, + ] as [String : Any] + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + } + } +} diff --git a/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointComment.swift b/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointComment.swift new file mode 100644 index 0000000..09b7448 --- /dev/null +++ b/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointComment.swift @@ -0,0 +1,130 @@ +// +// EndpointComment.swift +// QiscusCoreLite +// +// Created by Qiscus on 13/08/18. +// + +import Foundation + +// MARK: Comment API +internal enum APIComment { + case postComment(topicId: String, type: String, message: String, payload: [String:Any]?, extras: [String:Any]?, uniqueTempId: String?) + case loadComment(topicId: String, lastCommentId: Int?, after: Bool?, limit: Int?) + case delete(commentUniqueId: [String]) + case updateStatus(roomId: String,lastCommentReadId: String?, lastCommentReceivedId: String?) + case clear(roomChannelIds: [String]) + /// Search comment on server + case search(keyword: String, roomID: String?, lastCommentID: Int?) + case statusComment(id: String) +} + +extension APIComment : EndPoint { + var path: String { + switch self { + case .postComment: + return "/post_comment" + case .loadComment: + return "/load_comments" + case .delete( _): + return "/delete_messages" + case .updateStatus( _, _, _): + return "/update_comment_status" + case .clear( _): + return "/clear_room_messages" + case .search: + return "/search_messages" + case .statusComment(_): + return "/comment_receipt" + } + } + + var httpMethod: HTTPMethod { + switch self { + case .loadComment, .statusComment(_): + return .get + case .postComment, .updateStatus, .search( _, _, _): + return .post + case .delete, .clear( _): + return .delete + } + } + + var task: HTTPTask { + switch self { + case .postComment(let topicId, let type, let message, let payload, let extras, let uniqueTempId): + var params = [ + "topic_id" : topicId, + "type" : type, + "comment" : message + ] as [String : Any] + if let payloadParams = payload { + params["payload"] = payloadParams + } + if let extrasParams = extras { + params["extras"] = extrasParams + } + if let uniquetempid = uniqueTempId { + params["unique_temp_id"] = uniquetempid + } + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .loadComment(let topicId, let lastCommentId,let after,let limit): + var params = [ + "topic_id" : topicId + ] as [String : Any] + + if let lastcommentid = lastCommentId { + params["last_comment_id"] = lastcommentid + } + if let aftr = after { + params["after"] = aftr + } + if let limt = limit { + params["limit"] = limt + } + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + case .delete(let id): + var params = [ + "unique_ids" : id, + "is_hard_delete" : true, + "is_delete_for_everyone" : true + ] as [String : Any] + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .updateStatus(let roomId,let lastCommentReadId,let lastCommentReceivedId): + var params = [ + "room_id" : roomId + ] as [String : Any] + + if let lastcommentreadid = lastCommentReadId { + params["last_comment_read_id"] = lastcommentreadid + } + + if let lastcommentreceivedid = lastCommentReceivedId { + params["last_comment_received_id"] = lastcommentreceivedid + } + + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .clear(let roomChannelIds): + let params = [ + "room_channel_ids" : roomChannelIds + ] as [String : Any] + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .search(let keyword, let roomID, let lastCommentID): + var params = [ + "query" : keyword, + ] as [String : Any] + if let id = roomID { + params["room_id"] = id + } + if let commentID = lastCommentID { + params["last_comment_id"] = commentID + } + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .statusComment(let id): + let params = [ + "comment_id" : id, + ] as [String : Any] + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + } + } +} diff --git a/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointRoom.swift b/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointRoom.swift new file mode 100644 index 0000000..043a837 --- /dev/null +++ b/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointRoom.swift @@ -0,0 +1,198 @@ +// +// EndpointRoom.swift +// QiscusCoreLite +// +// Created by Qiscus on 13/08/18. +// + +import Foundation + +// MARK: Room API +internal enum APIRoom { + case roomList(showParticipants: Bool, limit: Int?, page: Int?, roomType: RoomType? , showRemoved: Bool, showEmpty: Bool) + case roomInfo(roomId: [String]?, roomUniqueId: [String]?, showParticipants: Bool, showRemoved: Bool) + case createNewRoom(name: String,participants: [String],avatarUrl: URL?, options: String?) + case updateRoom(roomId: String, roomName: String?, avatarUrl: URL?, options: String?) + case roomWithTarget(email: [String], options: String?) + case channelWithUniqueId(uniqueId: String,name: String?, avatarUrl: URL?, options: String?) + case addParticipant(roomId: String, emails: [String]) + case removeParticipant(roomId: String, emails: [String]) + case getParticipant(roomId: String, page:Int?, limit: Int?, offset: Int?, sorting : SortType?) + case getRoomById(roomId: String) +} + +extension APIRoom : EndPoint { + var path: String { + switch self { + case .roomList( _, _, _, _, _, _): + return "/user_rooms" + case .roomInfo( _, _, _, _): + return "/rooms_info" + case .createNewRoom( _, _, _, _): + return "/create_room" + case .updateRoom( _, _, _, _): + return "/update_room" + case .roomWithTarget( _, _): + return "/get_or_create_room_with_target" + case .channelWithUniqueId( _, _, _, _): + return "/get_or_create_room_with_unique_id" + case .addParticipant( _, _): + return "/add_room_participants" + case .removeParticipant( _, _): + return "/remove_room_participants" + case .getRoomById( _): + return "/get_room_by_id" + case .getParticipant(_): + return "/room_participants" + } + } + + var httpMethod: HTTPMethod { + switch self { + case .roomList, .getRoomById, .getParticipant: + return .get + case .roomInfo, .createNewRoom, .updateRoom, .roomWithTarget, .channelWithUniqueId, .addParticipant, .removeParticipant: + return .post + } + } + + var task: HTTPTask { + switch self { + case .roomList(let showParticipants,let limit, let page, let roomType, let showRemoved, let showEmpty): + var params = [ + "show_participants" : showParticipants, + "show_removed" : showRemoved, + "show_empty" : showEmpty + + ] as [String : Any] + + if let pages = page { + params["page"] = pages + } + + if let l = limit { + params["limit"] = l + } + + if let roomTypeParams = roomType { + params["room_type"] = roomTypeParams + } + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + case .roomInfo(let roomId, let roomUniqueId ,let showParticipants, let showRemoved): + var params = [ + "show_participants" : showParticipants, + "show_removed" : showRemoved + ]as [String : Any] + + if let id = roomId { + params["room_id"] = id + } + + if let uniqueId = roomUniqueId{ + params["room_unique_id"] = uniqueId + } + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .createNewRoom(let name,let participants,let avatarUrl, let options): + var params = [ + "name" : name, + "participants" : participants + ]as [String : Any] + + if let avatarurl = avatarUrl?.absoluteString { + params["avatar_url"] = avatarurl + } + + if let optionsParam = options { + params["options"] = optionsParam + } + + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .updateRoom(let id,let roomName,let avatarUrl, let options) : + var params = [ + "id" : id, + ]as [String : Any] + + if let roomname = roomName { + params["room_name"] = roomname + } + + if let avatarurl = avatarUrl?.absoluteString { + params["avatar_url"] = avatarurl + } + + if let optionsParam = options { + params["options"] = optionsParam + } + + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .roomWithTarget(let email, let options) : + var params = [ + "emails" : email + ] as [String : Any] + + if let optionsParams = options { + params["options"] = optionsParams + } + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .channelWithUniqueId(let uniqueId, let name, let avatarUrl, let options): + var params = [ + "unique_id" : uniqueId + ] + + if let nm = name { + params["name"] = nm + } + + if let avatarurl = avatarUrl?.absoluteString { + params["avatar_url"] = avatarurl + } + + if let optionsParams = options { + params["options"] = optionsParams + } + + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + + case .addParticipant(let roomId,let emails) : + let params = [ + "room_id" : roomId, + "emails" : emails + ] as [String : Any] + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .removeParticipant(let roomId,let emails) : + let params = [ + "room_id" : roomId, + "emails" : emails + ] as [String : Any] + return .requestParameters(bodyParameters: params, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .getRoomById(let roomId): + let params = [ + "id" : roomId ?? "" + ] as [String : Any] + + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + case .getParticipant(let roomId, let page, let limit, let offset, let sorting): + var params = [ + "room_unique_id" : roomId + ] as [String : Any] + + if let offset = offset { + params["offset"] = offset + } + + if let sorting = sorting { + params["sorting"] = sorting + } + + if let page = page { + params["page"] = page + } + + if let limit = limit { + params["limit"] = limit + } + + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + } + } +} diff --git a/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointUser.swift b/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointUser.swift new file mode 100644 index 0000000..dfee329 --- /dev/null +++ b/Source/QiscusCoreApi/Network/Service/Endpoint/EndpointUser.swift @@ -0,0 +1,85 @@ +// +// EndpointUser.swift +// QiscusCoreLite +// +// Created by Qiscus on 13/08/18. +// + +import Foundation + +// MARK: User API +internal enum APIUser { + case block(email: String) + case unblock(email: String) + case listBloked(page: Int?, limit: Int?) + case unread + case getUsers(page: Int?, limit: Int?, querySearch: String?) +} + +extension APIUser : EndPoint { + var path: String { + switch self { + case .block( _): + return "/block_user" + case .unblock( _): + return "/unblock_user" + case .listBloked( _, _): + return "/get_blocked_users" + case .unread: + return "/total_unread_count" + case .getUsers( _, _, _): + return "/get_user_list" + } + } + + var httpMethod: HTTPMethod { + switch self { + case .block, .unblock : + return .post + case .listBloked, .unread, .getUsers: + return .get + } + } + + var task: HTTPTask { + switch self { + case .block(let email): + let param = [ + "user_email" : email + ] + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .unblock(let email): + let param = [ + "user_email" : email + ] + return .requestParameters(bodyParameters: param, bodyEncoding: .jsonEncoding, urlParameters: nil) + case .listBloked(let page,let limit): + var params = [String : Any]() + if let p = page { + params["page"] = p + } + if let l = limit { + params["limit"] = l + } + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + case .unread: + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: nil) + case .getUsers(let page,let limit, let querySearch): + var params = [ + "order_query" : "username asc", + ] as [String : Any] + if let p = page { + params["page"] = p + } + if let l = limit { + params["limit"] = l + } + + if let s = querySearch { + params["query"] = s + } + + return .requestParameters(bodyParameters: nil, bodyEncoding: .jsonUrlEncoding, urlParameters: params) + } + } +} diff --git a/Source/QiscusCoreApi/Network/Service/HTTPUtil.swift b/Source/QiscusCoreApi/Network/Service/HTTPUtil.swift new file mode 100644 index 0000000..d78fb8d --- /dev/null +++ b/Source/QiscusCoreApi/Network/Service/HTTPUtil.swift @@ -0,0 +1,35 @@ +// +// HTTPMethod.swift +// QiscusCoreLite +// +// Created by Qiscus on 18/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +#if os(iOS) +import UIKit +#endif +import Foundation + +typealias HTTPHeaders = [String:String] + +internal enum HTTPMethod : String { + case get = "GET" + case post = "POST" + case put = "PUT" + case patch = "PATCH" + case delete = "DELETE" +} + +internal enum HTTPTask { + case request + + case requestParameters(bodyParameters: Parameters?, + bodyEncoding: ParameterEncoding, + urlParameters: Parameters?) + + case requestParametersAndHeaders(bodyParameters: Parameters?, + bodyEncoding: ParameterEncoding, + urlParameters: Parameters?, + additionHeaders: HTTPHeaders?) +} diff --git a/Source/QiscusCoreApi/Network/Service/Router.swift b/Source/QiscusCoreApi/Network/Service/Router.swift new file mode 100644 index 0000000..277113e --- /dev/null +++ b/Source/QiscusCoreApi/Network/Service/Router.swift @@ -0,0 +1,138 @@ +// +// Router.swift +// QiscusCoreLite +// +// Created by Malcolm Kumwenda on 2018/03/07. +// Copyright © 2018 Malcolm Kumwenda. All rights reserved. +// + +#if os(iOS) +import UIKit +#endif +import Foundation + +internal typealias NetworkRouterCompletion = (_ data: Data?,_ response: URLResponse?,_ error: Error?)->() + +protocol NetworkRouter: class { + associatedtype endPoint: EndPoint + func request(_ route: endPoint, completion: @escaping NetworkRouterCompletion) + func cancel() +} + +class Router: NetworkRouter { + private let session = URLSession(configuration: .default) + private var task: URLSessionTask? + let core: QiscusCoreAPI! + + var header : [String: String] { + get { + var headers = [ + "QISCUS-SDK-PLATFORM": "iOS", + "QISCUS-SDK-DEVICE-BRAND": "Apple", + "QISCUS-SDK-VERSION": VERSION_NUMBER, + "QISCUS-SDK-DEVICE-MODEL" : UIDevice.modelName, + "QISCUS-SDK-DEVICE-OS-VERSION" : UIDevice.current.systemVersion, + "QISCUS-SDK-APP-ID" : core.config.appId + ] + + if let user = core.userProfile { + if !user.token.isEmpty { + headers["QISCUS-SDK-TOKEN"] = user.token + } + if !user.id.isEmpty { + headers["QISCUS-SDK-USER-ID"] = user.id + } + } + +// if let customHeader = self.qiscusCore?.config.customHeader { +// headers.merge(customHeader as! [String : String]){(_, new) in new} +// } + + return headers + } + } + + init(core: QiscusCoreAPI) { + self.core = core + } + + func request(_ route: endpoint, completion: @escaping NetworkRouterCompletion) { + DispatchQueue.global(qos: .background).sync { + do { + let request = try self.buildRequest(from: route) + QiscusLogger.networkLogger(request: request) + self.task = self.session.dataTask(with: request, completionHandler: { data, response, error in + QiscusLogger.networkLogger(request: request, response: data) + DispatchQueue.main.sync { completion(data, response, error) } + }) + }catch { + DispatchQueue.main.sync { completion(nil, nil, error) } + } + self.task?.resume() + } + } + + func cancel() { + self.task?.cancel() + } + + fileprivate func buildRequest(from route: endpoint) throws -> URLRequest { + + var request = URLRequest(url: core.config.server.url.appendingPathComponent(route.path), + cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, + timeoutInterval: 10.0) + + request.httpMethod = route.httpMethod.rawValue + self.addAdditionalHeaders(header, request: &request) + + + do { + switch route.task { + case .request: + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + case .requestParameters(let bodyParameters, + let bodyEncoding, + let urlParameters): + + try self.configureParameters(bodyParameters: bodyParameters, + bodyEncoding: bodyEncoding, + urlParameters: urlParameters, + request: &request) + + case .requestParametersAndHeaders(let bodyParameters, + let bodyEncoding, + let urlParameters, + let additionalHeaders): + + self.addAdditionalHeaders(additionalHeaders, request: &request) + try self.configureParameters(bodyParameters: bodyParameters, + bodyEncoding: bodyEncoding, + urlParameters: urlParameters, + request: &request) + } + return request + } catch { + throw error + } + } + + fileprivate func configureParameters(bodyParameters: Parameters?, + bodyEncoding: ParameterEncoding, + urlParameters: Parameters?, + request: inout URLRequest) throws { + do { + try bodyEncoding.encode(urlRequest: &request, + bodyParameters: bodyParameters, urlParameters: urlParameters) + } catch { + throw error + } + } + + fileprivate func addAdditionalHeaders(_ additionalHeaders: HTTPHeaders?, request: inout URLRequest) { + guard let headers = additionalHeaders else { return } + for (key, value) in headers { + request.setValue(value, forHTTPHeaderField: key) + } + } + +} diff --git a/Source/QiscusCoreApi/Protocol/Interface.swift b/Source/QiscusCoreApi/Protocol/Interface.swift new file mode 100644 index 0000000..b110bc2 --- /dev/null +++ b/Source/QiscusCoreApi/Protocol/Interface.swift @@ -0,0 +1,29 @@ +// +// Interface.swift +// Pods +// +// Created by asharijuang on 05/02/20. +// + +import Foundation + +/// General Config and Auth +protocol CoreCommand { + func getJWTNonce(onSuccess: @escaping (QNonce) -> Void, onError: @escaping (QError) -> Void) + func setUserWithIdentityToken(token: String, onSuccess: @escaping (UserModel) -> Void, onError: @escaping (QError) -> Void) + func register(deviceToken token: String, isDevelopment: Bool, onSuccess: @escaping (Bool) -> Void, onError: @escaping (QError) -> Void) + func signOut() +} + +protocol RoomCommand { + func getChatRoom(id: String, onSuccess: @escaping (RoomModel, [CommentModel]) -> Void, onError: @escaping (QError) -> Void) + func userRooms(showParticipant: Bool , limit: Int, page: Int, roomType: RoomType, showRemoved: Bool, showEmpty: Bool, onSuccess: @escaping ([RoomModel], Meta) -> Void, onError: @escaping (QError) -> Void) +} + +protocol MessageCommand { + func loadMore(lastMessage message: CommentModel, limit: Int, onSuccess: @escaping ([CommentModel]) -> Void, onError: @escaping (QError) -> Void) + func send(message: CommentModel, onSuccess: @escaping (CommentModel) -> Void, onError: @escaping (QError) -> Void) + func markAsRead(message: CommentModel) + func newMessage() -> CommentModel + func delete(message: CommentModel, onSuccess: @escaping (CommentModel) -> Void, onError: @escaping (QError) -> Void) +} diff --git a/Source/QiscusCoreApi/Protocol/QiscusConnectionDelegate.swift b/Source/QiscusCoreApi/Protocol/QiscusConnectionDelegate.swift new file mode 100644 index 0000000..322fa37 --- /dev/null +++ b/Source/QiscusCoreApi/Protocol/QiscusConnectionDelegate.swift @@ -0,0 +1,136 @@ +// +// QiscusConnectionDelegate.swift +// QiscusCoreLite +// +// Created by Qiscus on 16/07/18. +// Copyright © 2018 Qiscus. All rights reserved. +// + +#if os(iOS) +import UIKit +#endif +import Foundation + +public protocol QiscusConnectionDelegate { + func connectionState(change state: QiscusConnectionState) + func onConnected() + func onReconnecting() + func onDisconnected(withError err: QError?) +} + +public enum QiscusConnectionState : String{ + case connecting = "connecting" + case connected = "connected" + case disconnected = "disconnected" +} + +public protocol QiscusCoreLiteDelegate { + // MARK: Event Room List + + /// new comment is comming + /// + /// - Parameters: + /// - room: room where event happen + /// - comment: new comment object + func onRoomMessageReceived(_ room: RoomModel, message: CommentModel) + + /// Deleted Comment + /// + /// - Parameter comment: comment deleted + func onRoomMessageDeleted(room: RoomModel, message: CommentModel) + + /// comment status change + /// + /// - Parameters: + /// - comment: new comment where status is change, you can compare from local data + /// - status: comment status, exp: deliverd, receipt, or read. + /// special case for read, for example we have message 1,2,3,4,5 then you got status change for message 5 it's mean message 1-4 has been read + @available(*, deprecated, message: "will soon become unavailable.") + func onRoomDidChangeComment(comment: CommentModel, changeStatus status: CommentStatus) + + /// comment status change to Delivered + /// + /// - Parameters: + /// - comment: new comment where status is change, you can compare from local data + func onRoomMessageDelivered(message : CommentModel) + + /// comment status change to Read + /// + /// - Parameters: + /// - comment: new comment where status is change, you can compare from local data + func onRoomMessageRead(message : CommentModel) + + /// Room update + /// + /// - Parameter room: new room object + @available(*, deprecated, message: "will soon become unavailable.") + func onRoom(update room: RoomModel) + + /// Deleted room + /// + /// - Parameter room: object room + @available(*, deprecated, message: "will soon become unavailable.") + func onRoom(deleted room: RoomModel) + + func gotNew(room: RoomModel) + + func onChatRoomCleared(roomId : String) +} + +public protocol QiscusCoreLiteRoomDelegate { + // MARK: Comment Event in Room + + /// new comment is comming + /// + /// - Parameters: + /// - comment: new comment object + func onMessageReceived(message: CommentModel) + + /// comment status change + /// + /// - Parameters: + /// - comment: new comment where status is change, you can compare from local data + /// - status: comment status, exp: deliverd, receipt, or read. + /// special case for read, for example we have message 1,2,3,4,5 then you got status change for message 5 it's mean message 1-4 has been read + @available(*, deprecated, message: "will soon become unavailable.") + func didComment(comment: CommentModel, changeStatus status: CommentStatus) + + /// comment status change to Delivered + /// + /// - Parameters: + /// - comment: new comment where status is change, you can compare from local data + func onMessageDelivered(message : CommentModel) + + /// comment status change to Read + /// + /// - Parameters: + /// - comment: new comment where status is change, you can compare from local data + func onMessageRead(message : CommentModel) + + /// Deleted Comment + /// + /// - Parameter comment: comment deleted + + func onMessageDeleted(message: CommentModel) + + /// User Typing Indicator + /// + /// - Parameters: + /// - user: object user or participant + /// - typing: true if user start typing and false when finish typin + func onUserTyping(userId : String, roomId : String, typing: Bool) + + /// User Online status + /// + /// - Parameters: + /// - userId: string of userId + /// - isOnline: true if user is online + /// - lastSeen: millisecond UTC + func onUserOnlinePresence(userId: String, isOnline: Bool, lastSeen: Date) + + /// Room update + /// + /// - Parameter room: new room object + @available(*, deprecated, message: "will soon become unavailable.") + func onRoom(update room: RoomModel) +} diff --git a/Source/QiscusCoreApi/QiscusCore.swift b/Source/QiscusCoreApi/QiscusCore.swift new file mode 100644 index 0000000..019064b --- /dev/null +++ b/Source/QiscusCoreApi/QiscusCore.swift @@ -0,0 +1,253 @@ +// +// QiscusChatSDK.swift +// QiscusChatSDK +// +// Created by asharijuang on 27/01/20. +// Copyright © 2020 qiscus. All rights reserved. +// + +import Foundation + +let VERSION_NUMBER = "0.2.1" + +public class QiscusCoreAPI { + + static var bundle:Bundle{ + get{ + let podBundle = Bundle(for: QiscusCoreAPI.self) + + if let bundleURL = podBundle.url(forResource: "SDK", withExtension: "bundle") { + return Bundle(url: bundleURL)! + }else{ + return podBundle + } + } + } + + public private(set) var config : QiscusConfig! + private var network : NetworkManager! + static var enableDebugPrint: Bool = false + + /// Logined + public var isLogined : Bool { + return self.userProfile != nil + } + + /// Can't set to nil + public private(set) var userProfile : UserModel? { + set { + if let user = newValue { + user.save(appID: self.config.appId) + } + } + get { + var user = UserModel.init() + user.loadUserProfile(appID: self.config.appId) + return user.token.isEmpty ? nil : user + } + } + + public init(withAppId id: String, server: QiscusServer? = nil) { + let defaultURL = URL(string: "https://api.qiscus.com")! + self.network = NetworkManager(core: self) + + let defaultServer = QiscusServer(url: defaultURL, realtimeURL: "", realtimePort: 80) + self.config = QiscusConfig(appId: id, server: defaultServer) + if let _server = server { + self.config.server = _server + } + } +} + +extension QiscusCoreAPI : CoreCommand { + public func register(deviceToken: String, isDevelopment: Bool, onSuccess: @escaping (Bool) -> Void, onError: @escaping (QError) -> Void) { + network.registerDeviceToken(deviceToken: deviceToken, isDevelopment: isDevelopment, onSuccess: onSuccess, onError: onError) + } + + public func signOut() { + if let user = self.userProfile { + user.clear(appID: self.config.appId) + }else { print("User no logined")} + } + + /// Login with Nonce or Idendity token + /// - Parameters: + /// - token: Identity token + /// - onSuccess: success login + /// - onError: error login + public func setUserWithIdentityToken(token: String, onSuccess: @escaping (UserModel) -> Void, onError: @escaping (QError) -> Void) { + network.login(identityToken: token, onSuccess: { (user) in + // save user profile + self.userProfile = user + // return success + onSuccess(user) + }, onError: onError) + } + + // MARK: Auth + /// Get JWTNonce from SDK server. use when login with JWT + /// - Parameter completion: @escaping with Optional(QNonce) and String Optional(error) + public func getJWTNonce(onSuccess: @escaping (QNonce) -> Void, onError: @escaping (QError) -> Void) { + network.getNonce(onSuccess: onSuccess, onError: onError) + } +} + +extension QiscusCoreAPI : RoomCommand { + /// User Rooms, support pagination + /// + /// - Parameters: + /// - showParticipant: Bool (true = include participants obj to the room, false = participants obj nil) + /// - limit: limit room per page + /// - page: page + /// - roomType: (single, group, public_channel) by default returning all type + /// - showRemoved: Bool (true = include room that has been removed, false = exclude room that has been removed) + /// - showEmpty: Bool (true = it will show all rooms that have been created event there are no messages, default is false where only room that have at least one message will be shown) + /// - onSuccess: response success + /// - onError: response error + public func userRooms(showParticipant: Bool, limit: Int, page: Int, roomType: RoomType, showRemoved: Bool, showEmpty: Bool, onSuccess: @escaping ([RoomModel], Meta) -> Void, onError: @escaping (QError) -> Void) { + network.getRoomList(showParticipant: showParticipant, limit: limit, page: page, roomType: roomType, showRemoved: showRemoved, showEmpty: showEmpty) { (rooms, meta, errorMessage) in + if let _rooms = rooms, let _meta = meta { + onSuccess(_rooms, _meta) + }else { + if let _error = errorMessage { + onError(QError(message: _error)) + }else { + onError(QError(message: "Unexpected Error")) + } + } + + } + } + + /// Get Object Room by id + /// - Parameters: + /// - id: room id, not unique id + /// - onSuccess: response success + /// - onError: response error + public func getChatRoom(id: String, onSuccess: @escaping (RoomModel, [CommentModel]) -> Void, onError: @escaping (QError) -> Void) { + network.getRoomById(roomId: id, onSuccess: { (room, comments) in + if let messages = comments { + onSuccess(room, messages) + }else { + onSuccess(room, [CommentModel]()) + } + }, onError: onError) + } +} + +extension QiscusCoreAPI : MessageCommand { + /// Load Previous message + /// - Parameters: + /// - message: your last/old message + /// - limit: number of limit message per request + /// - onSuccess: response success + /// - onError: response error + public func loadMore(lastMessage message: CommentModel, limit: Int, onSuccess: @escaping ([CommentModel]) -> Void, onError: @escaping (QError) -> Void) { + network.loadComments(roomId: message.roomId, lastCommentId: Int(message.id) ?? 0, after: false, limit: limit) { (comments, error) in + if let _comments = comments { + onSuccess(_comments) + }else { + if let _error = error { + onError(_error) + }else { + onError(QError(message: "Unexpected Error")) + } + } + } + } + + /// Delete Message + /// - Parameters: + /// - message: object message to be delete + /// - onSuccess: response success + /// - onError: response error + public func delete(message: CommentModel, onSuccess: @escaping (CommentModel) -> Void, onError: @escaping (QError) -> Void) { + + network.deleteComment(commentUniqueId: [message.uniqId]) { (comments, error) in + if let _comments = comments, let _comment = _comments.first { + onSuccess(_comment) + }else { + if let _error = error { + onError(_error) + }else { + onError(QError(message: "Unexpected Error")) + } + } + } + } + + + /// Long Polling system to get new message + /// - Parameters: + /// - lastMessageId: last message id + /// - onSuccess: response array of message when request success + /// - onError: response error + public func sync(lastMessageId: String, onSuccess: @escaping ([CommentModel])->Void, onError: @escaping (QError) -> Void) { + network.sync(lastCommentReceivedId: lastMessageId) { (comments, error) in + if let messages = comments { + onSuccess(messages) + }else { + if let errorMessage = error { + onError(QError(message: errorMessage)) + }else { + onError(QError(message: "New Message Unavailable")) + } + } + } + } + + /// Generate new message + public func newMessage() -> CommentModel { + guard let user = self.userProfile else { + fatalError("Need to login") + } + return CommentModel(user: user) + } + + /// Mark as read, to other patricipant + /// - Parameter message: object message + public func markAsRead(message: CommentModel) { + // In this case everytime you receive message, that's mead you read this message + network.updateCommentStatus(roomId: message.roomId, lastCommentReadId: message.id, lastCommentReceivedId: message.id) + } + + /// Send Message + /// - Parameters: + /// - message: Object message, you can generate by using newMessage + /// - onSuccess: response success + /// - onError: response error + public func send(message: CommentModel, onSuccess: @escaping (CommentModel) -> Void, onError: @escaping (QError) -> Void) { + // update comment + let _comment = message + _comment.roomId = message.roomId + _comment.status = .sending + _comment.timestamp = CommentModel.getTimestamp() + // check comment type, if not Qiscus Comment set as custom type + if !_comment.isQiscustype() { + let _payload = _comment.payload + let _type = _comment.type + _comment.type = "custom" + _comment.payload?.removeAll() // clear last payload then recreate + _comment.payload = ["type" : _type] + if let payload = _payload { + _comment.payload!["content"] = payload + }else { + _comment.payload!["content"] = ["":""] + } + } + + network.postComment(roomId: _comment.roomId, type: _comment.type, message: _comment.message, payload: _comment.payload, extras: _comment.extras, uniqueTempId: _comment.uniqId) { (data, error) in + + if let errorMessage = error { + onError(QError(message: errorMessage)) + }else { + if let newMessage = data { + onSuccess(newMessage) + }else { + onError(QError(message: "Failed to send message")) + } + } + + } + } +} diff --git a/QiscusCoreApi.framework/Headers/QiscusCoreApi.h b/Source/QiscusCoreApi/QiscusCoreAPI.h similarity index 100% rename from QiscusCoreApi.framework/Headers/QiscusCoreApi.h rename to Source/QiscusCoreApi/QiscusCoreAPI.h diff --git a/Source/QiscusCoreApi/QiscusCoreAPI.modulemap b/Source/QiscusCoreApi/QiscusCoreAPI.modulemap new file mode 100644 index 0000000..71fd7ca --- /dev/null +++ b/Source/QiscusCoreApi/QiscusCoreAPI.modulemap @@ -0,0 +1,8 @@ +framework module QiscusCoreAPI { + umbrella header QiscusCoreAPI.h + + export * + module * { + export * + } +} diff --git a/Source/QiscusCoreApi/SupportingFiles/QiscusCoreAPI.modulemap b/Source/QiscusCoreApi/SupportingFiles/QiscusCoreAPI.modulemap new file mode 100644 index 0000000..b0dd45b --- /dev/null +++ b/Source/QiscusCoreApi/SupportingFiles/QiscusCoreAPI.modulemap @@ -0,0 +1,8 @@ +framework module QiscusCoreAPI { + umbrella header "QiscusCoreAPI.h" + + export * + module * { + export * + } +}