Skip to content

Commit

Permalink
Swift example module for XEP-0048
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisballinger committed Nov 15, 2017
1 parent 577ee85 commit fd4bd07
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 8 deletions.
6 changes: 3 additions & 3 deletions Categories/NSXMLElement+XMPP.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ NS_ASSUME_NONNULL_BEGIN
* Extracting a single element.
**/

- (nullable NSXMLElement *)elementForName:(NSString *)name;
- (nullable NSXMLElement *)elementForName:(NSString *)name xmlns:(NSString *)xmlns;
- (nullable NSXMLElement *)elementForName:(NSString *)name xmlnsPrefix:(NSString *)xmlnsPrefix;
- (nullable NSXMLElement *)elementForName:(NSString *)name NS_SWIFT_NAME(element(forName:));
- (nullable NSXMLElement *)elementForName:(NSString *)name xmlns:(NSString *)xmlns NS_SWIFT_NAME(element(forName:xmlns:));
- (nullable NSXMLElement *)elementForName:(NSString *)name xmlnsPrefix:(NSString *)xmlnsPrefix NS_SWIFT_NAME(element(forName:xmlnsPrefix:));

/**
* Convenience methods for removing child elements.
Expand Down
1 change: 1 addition & 0 deletions Core/XMPPModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly) void *moduleQueueTag;
@property (strong, readonly, nullable) XMPPStream *xmppStream;
@property (nonatomic, readonly) NSString *moduleName;
@property (nonatomic, readonly) id multicastDelegate;

- (instancetype)init;
- (instancetype)initWithDispatchQueue:(nullable dispatch_queue_t)queue;
Expand Down
1 change: 1 addition & 0 deletions Core/XMPPModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


@implementation XMPPModule
@synthesize multicastDelegate, moduleQueue, xmppStream, moduleQueueTag;

/**
* Standard init method.
Expand Down
2 changes: 1 addition & 1 deletion Extensions/XEP-0048/NSXMLElement+XEP_0048.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@implementation NSXMLElement (XEP_0048)

- (XMPPBookmarksStorageElement*) bookmarksStorageElement {
NSXMLElement *element = [NSXMLElement elementWithName:XMPPBookmarkConstants.storageElement xmlns:XMPPBookmarkConstants.xmlns];
NSXMLElement *element = [self elementForName:XMPPBookmarkConstants.storageElement xmlns:XMPPBookmarkConstants.xmlns];
return [XMPPBookmarksStorageElement bookmarksStorageElementFromElement:element];
}
@end
Expand Down
139 changes: 139 additions & 0 deletions Swift/XEP-0048/XMPPBookmarksModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//
// XMPPBookmarksModule.swift
// XMPPFramework
//
// Created by Chris Ballinger on 11/14/17.
// Copyright © 2017 Chris Ballinger. All rights reserved.
//

import Foundation
import CocoaLumberjackSwift

@objc public enum XMPPBookmarksMode: Int {
/// Private XML Storage (XEP-0049)
/// https://xmpp.org/extensions/xep-0049.html
case privateXmlStorage

/// Recommended by spec, but unimplemented
// case pubsub
}

@objc public protocol XMPPBookmarksDelegate: NSObjectProtocol {
@objc optional func xmppBookmarks(_ sender: XMPPBookmarksModule, didRetrieve bookmarks: [XMPPBookmark], responseIq: XMPPIQ)
@objc optional func xmppBookmarks(_ sender: XMPPBookmarksModule, didNotRetrieveBookmarks errorIq: XMPPIQ?)

@objc optional func xmppBookmarks(_ sender: XMPPBookmarksModule, didPublish bookmarks: [XMPPBookmark], responseIq: XMPPIQ)
@objc optional func xmppBookmarks(_ sender: XMPPBookmarksModule, didNotPublishBookmarks errorIq: XMPPIQ?)
}

/// XEP-0048: Booksmarks
///
/// This specification defines an XML data format for use by XMPP clients in storing bookmarks to mult-user chatrooms and web pages. The chatroom bookmarking function includes the ability to auto-join rooms on login.
/// https://xmpp.org/extensions/xep-0048.html
public class XMPPBookmarksModule: XMPPModule {

// MARK: - Properties
@objc public let mode: XMPPBookmarksMode
private var tracker: XMPPIDTracker?

// MARK: - Init

/// Right now there's only one mode (privateXmlStorage)
@objc public init(mode: XMPPBookmarksMode,
dispatchQueue: DispatchQueue? = nil) {
self.mode = mode
super.init(dispatchQueue: dispatchQueue)
}

// MARK: - XMPPModule Overrides
override public func activate(_ xmppStream: XMPPStream) -> Bool {
guard super.activate(xmppStream) else {
return false
}
performBlockAsync {
self.tracker = XMPPIDTracker(stream: self.xmppStream, dispatchQueue: self.moduleQueue)
}
return true
}

override public func deactivate() {
performBlock {
self.tracker?.removeAllIDs()
self.tracker = nil
}
super.deactivate()
}

// MARK: - Public API

/// Fetches all of your stored bookmarks on the server
@objc public func fetchBookmarks() {
let storage = XMLElement.storage
let query = XMLElement.query(child: storage)
let get = XMPPIQ(iqType: .get, child: query)

let handler = { (_ obj: Any, _ info: XMPPTrackingInfo) in
guard let responseIq = obj as? XMPPIQ,
let iqType = responseIq.iqType,
let query = responseIq.query,
let storage = query.bookmarksStorageElement,
iqType != .error else {
self.multicast.xmppBookmarks!(self, didNotRetrieveBookmarks: obj as? XMPPIQ)
return
}

let bookmarks = storage.bookmarks
self.multicast.xmppBookmarks!(self, didRetrieve: bookmarks, responseIq: responseIq)
}
performBlockAsync {
self.tracker?.add(get, block: handler, timeout: 30.0)
self.xmppStream?.send(get)
}
}
}

// MARK: - XMPPStreamDelegate

extension XMPPBookmarksModule: XMPPStreamDelegate {
public func xmppStream(_ sender: XMPPStream, didReceive iq: XMPPIQ) -> Bool {
guard let type = iq.iqType,
type == .result || type == .error else {
return false
}
var success = false
// Some error responses for self or contacts don't have a "from"
if iq.from == nil, let eid = iq.elementID {
success = tracker?.invoke(forID: eid, with: iq) ?? false
} else {
success = tracker?.invoke(for: iq, with: iq) ?? false
}
return success
}
}

// MARK: - Private Extensions
private extension XMPPIQ {
var query: XMLElement? {
return element(forName: PrivateXmlStorageConstants.queryElement, xmlns: PrivateXmlStorageConstants.xmlns)
}
}

private extension XMLElement {
static var storage: XMLElement {
return XMLElement(name: XMPPBookmarkConstants.storageElement, xmlns: XMPPBookmarkConstants.xmlns)
}

static func query(child: XMLElement? = nil) -> XMLElement {
let query = XMLElement(name: PrivateXmlStorageConstants.queryElement, xmlns: PrivateXmlStorageConstants.xmlns)
if let child = child {
query.addChild(child)
}
return query
}
}

// MARK: - Constants
fileprivate struct PrivateXmlStorageConstants {
static let xmlns = "jabber:iq:private"
static let queryElement = "query"
}
31 changes: 31 additions & 0 deletions Swift/XMPPIQ.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// XMPPIQ.swift
// XMPPFramework
//
// Created by Chris Ballinger on 11/14/17.
// Copyright © 2017 XMPPFramework. All rights reserved.
//

import Foundation

public extension XMPPIQ {
public enum IQType: String {
case get
case set
case result
case error
}

public var iqType: IQType? {
guard let type = self.type else { return nil }
let iqType = IQType(rawValue: type)
return iqType
}

public convenience init(iqType: IQType,
to JID: XMPPJID? = nil,
elementID eid: String? = nil,
child childElement: XMLElement? = nil) {
self.init(type: iqType.rawValue, to: JID, elementID: eid, child: childElement)
}
}
27 changes: 27 additions & 0 deletions Swift/XMPPModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// XMPPModule.swift
// XMPPFramework
//
// Created by Chris Ballinger on 11/14/17.
// Copyright © 2017 XMPPFramework. All rights reserved.
//

import Foundation

public extension XMPPModule {
/**
* This helper helps smooth things over with the multicastDelegate.
* Normally you'd have to downcast 'Any' to 'AnyObject' every time
* you want to send a message to the multicastDelegate.
*
* Note: You must use '!' instead of '?' otherwise the invocation will be ignored.
*
* For example, in your XMPPModule subclass:
*
* multicast.xmppBookmarks!(self, didRetrieve: bookmarks, responseIq: responseIq)
*
*/
public var multicast: AnyObject {
return multicastDelegate as AnyObject
}
}
36 changes: 34 additions & 2 deletions XMPPFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@
D96D6EA51F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m in Sources */ = {isa = PBXBuildFile; fileRef = D96D6EA11F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m */; };
D96D6EA61F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m in Sources */ = {isa = PBXBuildFile; fileRef = D96D6EA11F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m */; };
D96D6EA71F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m in Sources */ = {isa = PBXBuildFile; fileRef = D96D6EA11F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m */; };
D97BE9EF1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9EE1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift */; };
D97BE9F01FBBACEA00CFE3BB /* XMPPBookmarksModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9EE1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift */; };
D97BE9F11FBBACEA00CFE3BB /* XMPPBookmarksModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9EE1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift */; };
D97BE9F51FBBCAA000CFE3BB /* XMPPModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9F41FBBC82D00CFE3BB /* XMPPModule.swift */; };
D97BE9F61FBBCAA000CFE3BB /* XMPPModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9F41FBBC82D00CFE3BB /* XMPPModule.swift */; };
D97BE9F71FBBCAA100CFE3BB /* XMPPModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9F41FBBC82D00CFE3BB /* XMPPModule.swift */; };
D97BE9FE1FBBE4E200CFE3BB /* XMPPIQ.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9FD1FBBE4E200CFE3BB /* XMPPIQ.swift */; };
D97BE9FF1FBBE4E200CFE3BB /* XMPPIQ.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9FD1FBBE4E200CFE3BB /* XMPPIQ.swift */; };
D97BEA001FBBE4E200CFE3BB /* XMPPIQ.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97BE9FD1FBBE4E200CFE3BB /* XMPPIQ.swift */; };
D9A7A9B91FB97892005183CD /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D9DCD3A71E6251FA0010D1C7 /* libresolv.tbd */; };
D9A7A9BA1FB97892005183CD /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D9DCD3A51E6251E30010D1C7 /* libxml2.tbd */; };
D9A7A9BB1FB97892005183CD /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9DCD35D1E6250CA0010D1C7 /* CocoaLumberjack.framework */; };
Expand Down Expand Up @@ -1480,6 +1489,9 @@
D96D6E991F8D9D7E006DEC58 /* XMPPMessage+XEP_0359.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "XMPPMessage+XEP_0359.m"; sourceTree = "<group>"; };
D96D6EA01F8DA80A006DEC58 /* NSXMLElement+XEP_0359.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSXMLElement+XEP_0359.h"; sourceTree = "<group>"; };
D96D6EA11F8DA80A006DEC58 /* NSXMLElement+XEP_0359.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSXMLElement+XEP_0359.m"; sourceTree = "<group>"; };
D97BE9EE1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMPPBookmarksModule.swift; sourceTree = "<group>"; };
D97BE9F41FBBC82D00CFE3BB /* XMPPModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMPPModule.swift; sourceTree = "<group>"; };
D97BE9FD1FBBE4E200CFE3BB /* XMPPIQ.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMPPIQ.swift; sourceTree = "<group>"; };
D9A7AA621FB97892005183CD /* XMPPFrameworkSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = XMPPFrameworkSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D9A7AA741FB97908005183CD /* XMPPFrameworkSwift-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "XMPPFrameworkSwift-Info.plist"; sourceTree = "<group>"; };
D9A7AA751FB97908005183CD /* XMPPFramework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMPPFramework.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2047,12 +2059,23 @@
path = "XEP-0359";
sourceTree = "<group>";
};
D97BE9F21FBBACF300CFE3BB /* XEP-0048 */ = {
isa = PBXGroup;
children = (
D97BE9EE1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift */,
);
path = "XEP-0048";
sourceTree = "<group>";
};
D9A7AA731FB97908005183CD /* Swift */ = {
isa = PBXGroup;
children = (
D97BE9F41FBBC82D00CFE3BB /* XMPPModule.swift */,
D97BE9F21FBBACF300CFE3BB /* XEP-0048 */,
D9A7B0061FB97FE6005183CD /* XMPPFrameworkSwift.h */,
D9A7AA741FB97908005183CD /* XMPPFrameworkSwift-Info.plist */,
D9A7AA751FB97908005183CD /* XMPPFramework.swift */,
D97BE9FD1FBBE4E200CFE3BB /* XMPPIQ.swift */,
);
path = Swift;
sourceTree = "<group>";
Expand Down Expand Up @@ -4043,23 +4066,32 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D97BE9EF1FBBACEA00CFE3BB /* XMPPBookmarksModule.swift in Sources */,
D9A7B0161FB980E2005183CD /* XMPPFramework.swift in Sources */,
D97BE9FE1FBBE4E200CFE3BB /* XMPPIQ.swift in Sources */,
D97BE9F71FBBCAA100CFE3BB /* XMPPModule.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D9A7AD151FB97A3C005183CD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D97BE9F01FBBACEA00CFE3BB /* XMPPBookmarksModule.swift in Sources */,
D9A7B0171FB980E2005183CD /* XMPPFramework.swift in Sources */,
D97BE9FF1FBBE4E200CFE3BB /* XMPPIQ.swift in Sources */,
D97BE9F61FBBCAA000CFE3BB /* XMPPModule.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D9A7AE611FB97A65005183CD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D97BE9F11FBBACEA00CFE3BB /* XMPPBookmarksModule.swift in Sources */,
D9A7B0181FB980E2005183CD /* XMPPFramework.swift in Sources */,
D97BEA001FBBE4E200CFE3BB /* XMPPIQ.swift in Sources */,
D97BE9F51FBBCAA000CFE3BB /* XMPPModule.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -4769,7 +4801,7 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = org.robbiehanson.XMPPFramework;
PRODUCT_NAME = "$(PROJECT_NAME)Swift";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -4795,7 +4827,7 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = org.robbiehanson.XMPPFramework;
PRODUCT_NAME = "$(PROJECT_NAME)Swift";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
Loading

0 comments on commit fd4bd07

Please sign in to comment.