Skip to content

Commit fe6b7fc

Browse files
authored
Merge pull request #144 from tinode/next
Incognito mode
2 parents b6a594c + 262f965 commit fe6b7fc

File tree

8 files changed

+421
-284
lines changed

8 files changed

+421
-284
lines changed

TinodeSDK.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
0AAF8BEC23B70E2A00C9CFBB /* MsgRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AAF8BEB23B70E2A00C9CFBB /* MsgRange.swift */; };
3535
94CD577D9DA889C043928B4C /* Pods_TinodeSDKTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF75C7301A406947462E1C7D /* Pods_TinodeSDKTests.framework */; };
3636
9C88C579E43E9FD3687DD093 /* Pods_TinodeSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 679C08F81F0F4B80EE066C70 /* Pods_TinodeSDK.framework */; };
37+
B0876010243F1C3E00B464D1 /* MeTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B087600F243F1C3E00B464D1 /* MeTopic.swift */; };
38+
B0876012243F1DD900B464D1 /* FndTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0876011243F1DD900B464D1 /* FndTopic.swift */; };
39+
B0876014243F1E4000B464D1 /* ComTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0876013243F1E4000B464D1 /* ComTopic.swift */; };
3740
/* End PBXBuildFile section */
3841

3942
/* Begin PBXContainerItemProxy section */
@@ -79,6 +82,9 @@
7982
15EB4A3618442D5E79FEAB59 /* Pods-TinodeSDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TinodeSDK.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TinodeSDK/Pods-TinodeSDK.debug.xcconfig"; sourceTree = "<group>"; };
8083
679C08F81F0F4B80EE066C70 /* Pods_TinodeSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TinodeSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8184
79834550805DF1A314040328 /* Pods-TinodeSDKTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TinodeSDKTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TinodeSDKTests/Pods-TinodeSDKTests.debug.xcconfig"; sourceTree = "<group>"; };
85+
B087600F243F1C3E00B464D1 /* MeTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeTopic.swift; sourceTree = "<group>"; };
86+
B0876011243F1DD900B464D1 /* FndTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FndTopic.swift; sourceTree = "<group>"; };
87+
B0876013243F1E4000B464D1 /* ComTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComTopic.swift; sourceTree = "<group>"; };
8288
B0CD0F1036565AA6D7FCB418 /* Pods-TinodeSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TinodeSDK.release.xcconfig"; path = "./Pods/Target Support Files/Pods-TinodeSDK/Pods-TinodeSDK.release.xcconfig"; sourceTree = "<group>"; };
8389
B4211CF84B51039364CC4DD3 /* Pods-TinodeSDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TinodeSDK.debug.xcconfig"; path = "./Pods/Target Support Files/Pods-TinodeSDK/Pods-TinodeSDK.debug.xcconfig"; sourceTree = "<group>"; };
8490
BF75C7301A406947462E1C7D /* Pods_TinodeSDKTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TinodeSDKTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -132,8 +138,11 @@
132138
children = (
133139
0A44B00F2244AB0F00A98866 /* model */,
134140
0A431BF52244A14C00A837F7 /* AuthScheme.swift */,
141+
B0876013243F1E4000B464D1 /* ComTopic.swift */,
135142
0A431BF42244A14C00A837F7 /* Connection.swift */,
143+
B0876011243F1DD900B464D1 /* FndTopic.swift */,
136144
0A610452227563E200C6A9D2 /* Log.swift */,
145+
B087600F243F1C3E00B464D1 /* MeTopic.swift */,
137146
0A431BF22244A14C00A837F7 /* PromisedReply.swift */,
138147
0A431BF32244A14C00A837F7 /* RFC3339Format.swift */,
139148
0A431BF72244A14D00A837F7 /* Storage.swift */,
@@ -382,9 +391,11 @@
382391
0A431BFD2244A14D00A837F7 /* User.swift in Sources */,
383392
0A44B00C2244AAC500A98866 /* JSONValue.swift in Sources */,
384393
0A610453227563E200C6A9D2 /* Log.swift in Sources */,
394+
B0876012243F1DD900B464D1 /* FndTopic.swift in Sources */,
385395
0AAF8BEC23B70E2A00C9CFBB /* MsgRange.swift in Sources */,
386396
0A81B317236FA896009AF14B /* Types.swift in Sources */,
387397
0A44B0052244AAC500A98866 /* ServerMessages.swift in Sources */,
398+
B0876014243F1E4000B464D1 /* ComTopic.swift in Sources */,
388399
0A431BFC2244A14D00A837F7 /* AuthScheme.swift in Sources */,
389400
0A44B00E2244AAC500A98866 /* ClientMessages.swift in Sources */,
390401
0A44B00A2244AAC500A98866 /* Acs.swift in Sources */,
@@ -400,6 +411,7 @@
400411
0A431C002244A14D00A837F7 /* RFC3339Format.swift in Sources */,
401412
0A44B00D2244AAC500A98866 /* AcsHelper.swift in Sources */,
402413
0A431BFE2244A14D00A837F7 /* Storage.swift in Sources */,
414+
B0876010243F1C3E00B464D1 /* MeTopic.swift in Sources */,
403415
);
404416
runOnlyForDeploymentPostprocessing = 0;
405417
};

TinodeSDK/ComTopic.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// ComTopic.swift
3+
// TinodeSDK
4+
//
5+
// Copyright © 2020 Tinode. All rights reserved.
6+
//
7+
8+
import Foundation
9+
10+
public class ComTopic<DP: Codable & Mergeable>: Topic<DP, PrivateType, DP, PrivateType> {
11+
override init(tinode: Tinode?, name: String, l: Listener?) {
12+
super.init(tinode: tinode, name: name, l: l)
13+
}
14+
override init(tinode: Tinode?, sub: Subscription<DP, PrivateType>) {
15+
super.init(tinode: tinode, sub: sub)
16+
}
17+
override init(tinode: Tinode?, name: String, desc: Description<DP, PrivateType>) {
18+
super.init(tinode: tinode, name: name, desc: desc)
19+
}
20+
public convenience init(in tinode: Tinode?, forwardingEventsTo l: Listener? = nil) {
21+
self.init(tinode: tinode!, name: Tinode.kTopicNew + tinode!.nextUniqueString(), l: l)
22+
}
23+
24+
public override var isArchived: Bool {
25+
guard let archived = priv?["arch"] else { return false }
26+
switch archived {
27+
case .bool(let x):
28+
return x
29+
default:
30+
return false
31+
}
32+
}
33+
34+
public var comment: String? {
35+
return priv?.comment
36+
}
37+
38+
public var peer: Subscription<DP, PrivateType>? {
39+
guard isP2PType else { return nil }
40+
return self.getSubscription(for: self.name)
41+
}
42+
43+
override public func getSubscription(for key: String?) -> Subscription<DP, PrivateType>? {
44+
guard let sub = super.getSubscription(for: key) else { return nil }
45+
if isP2PType && sub.pub == nil {
46+
sub.pub = self.name == key ? self.pub : tinode?.getMeTopic()?.pub as? DP
47+
}
48+
return sub
49+
}
50+
51+
public func updateArchived(archived: Bool) -> PromisedReply<ServerMessage>? {
52+
var priv = PrivateType()
53+
priv.archived = archived
54+
let meta = MsgSetMeta<DP, PrivateType>(
55+
desc: MetaSetDesc(pub: nil, priv: priv),
56+
sub: nil,
57+
tags: nil,
58+
cred: nil)
59+
return setMeta(meta: meta)
60+
}
61+
}

TinodeSDK/FndTopic.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// FndTopic.swift
3+
// TinodeSDK
4+
//
5+
// Copyright © 2020 Tinode. All rights reserved.
6+
//
7+
8+
import Foundation
9+
10+
11+
public class FndTopic<SP: Codable>: Topic<String, String, SP, Array<String>> {
12+
init(tinode: Tinode?) {
13+
super.init(tinode: tinode, name: Tinode.kTopicFnd)
14+
}
15+
16+
@discardableResult
17+
override public func setMeta(meta: MsgSetMeta<String, String>) -> PromisedReply<ServerMessage>? {
18+
if self.subs != nil {
19+
self.subs!.removeAll()
20+
self.subs = nil
21+
self.subsLastUpdated = nil
22+
self.listener?.onSubsUpdated()
23+
}
24+
return super.setMeta(meta: meta)
25+
}
26+
27+
override func routeMetaSub(meta: MsgServerMeta) {
28+
if let subscriptions = meta.sub {
29+
for upd in subscriptions {
30+
var sub = getSubscription(for: upd.uniqueId)
31+
if sub != nil {
32+
_ = sub!.merge(sub: upd as! Subscription<SP, [String]>)
33+
} else {
34+
sub = upd as? Subscription<SP, [String]>
35+
self.addSubToCache(sub: sub!)
36+
}
37+
self.listener?.onMetaSub(sub: sub!)
38+
}
39+
}
40+
self.listener?.onSubsUpdated()
41+
}
42+
43+
override public func getSubscriptions() -> [Subscription<SP, Array<String>>]? {
44+
guard let v = subs?.values else { return nil }
45+
return Array(v)
46+
}
47+
48+
override func addSubToCache(sub: Subscription<SP, [String]>) {
49+
guard let unique = sub.user ?? sub.topic else { return }
50+
51+
if subs == nil {
52+
subs = [:]
53+
}
54+
subs![unique] = sub
55+
}
56+
}

TinodeSDK/MeTopic.swift

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
//
2+
// MeTopic.swift
3+
// TinodeSDK
4+
//
5+
// Copyright © 2020 Tinode. All rights reserved.
6+
//
7+
8+
import Foundation
9+
10+
11+
open class MeTopic<DP: Codable & Mergeable>: Topic<DP, PrivateType, DP, PrivateType> {
12+
public init(tinode: Tinode?, l: MeTopic<DP>.Listener?) {
13+
super.init(tinode: tinode, name: Tinode.kTopicMe, l: l)
14+
}
15+
public init(tinode: Tinode?, desc: Description<DP, PrivateType>) {
16+
super.init(tinode: tinode, name: Tinode.kTopicMe, desc: desc)
17+
}
18+
19+
override public var subsUpdated: Date? {
20+
get { return tinode?.topicsUpdated }
21+
}
22+
23+
override func loadSubs() -> Int {
24+
// Don't attempt to load subscriptions: 'me' subscriptions are stored as topics.
25+
return 0
26+
}
27+
28+
override public func topicLeft(unsub: Bool?, code: Int?, reason: String?) {
29+
super.topicLeft(unsub: unsub, code: code, reason: reason)
30+
if let topics = tinode?.getTopics() {
31+
for t in topics {
32+
t.online = false
33+
}
34+
}
35+
}
36+
37+
override public func updateMode(update: String) -> PromisedReply<ServerMessage>? {
38+
var acs = accessMode
39+
if acs == nil {
40+
acs = Acs()
41+
}
42+
43+
let mode = AcsHelper(ah: acs!.want)
44+
if mode.update(from: update) {
45+
return setSubscription(sub: MetaSetSub(user: nil, mode: mode.description))
46+
}
47+
48+
// The state is unchanged, return resolved promise.
49+
return PromisedReply<ServerMessage>(value: ServerMessage())
50+
}
51+
52+
override internal func update(acsMap: [String:String]?, sub: MetaSetSub) {
53+
var newAcs: Acs
54+
if let acsMap = acsMap {
55+
newAcs = Acs(from: acsMap)
56+
} else {
57+
newAcs = Acs()
58+
newAcs.want = AcsHelper(str: sub.mode)
59+
}
60+
61+
var changed = false
62+
var acs = accessMode
63+
if acs == nil {
64+
acs = newAcs
65+
changed = true
66+
} else {
67+
changed = acs!.merge(from: newAcs)
68+
}
69+
70+
if changed {
71+
accessMode = acs
72+
self.store?.topicUpdate(topic: self)
73+
}
74+
}
75+
76+
override public func routePres(pres: MsgServerPres) {
77+
let what = MsgServerPres.parseWhat(what: pres.what)
78+
if what == .kTerm {
79+
// The 'me' topic itself is detached. Mark as unsubscribed.
80+
super.routePres(pres: pres)
81+
return
82+
}
83+
84+
if what == .kUpd && Tinode.kTopicMe == pres.src {
85+
// Me's desc was updated, fetch the updated version.
86+
getMeta(query: getMetaGetBuilder().withDesc().build())
87+
return
88+
}
89+
90+
// "what":"tags" has src == nil
91+
if let topic = pres.src != nil ? tinode!.getTopic(topicName: pres.src!) : nil {
92+
switch what {
93+
case .kOn: // topic came online
94+
topic.online = true
95+
case .kOff: // topic went offline
96+
topic.online = false
97+
topic.lastSeen = LastSeen(when: Date(), ua: nil)
98+
case .kMsg: // new message received
99+
topic.seq = pres.seq
100+
if pres.act == nil || tinode!.isMe(uid: pres.act!) {
101+
assignRead(to: topic, read: pres.seq)
102+
}
103+
topic.touched = Date()
104+
case .kUpd: // pub/priv updated
105+
getMeta(query: getMetaGetBuilder().withSub(user: pres.src).build())
106+
case .kAcs: // access mode changed
107+
if topic.updateAccessMode(ac: pres.dacs) {
108+
self.store?.topicUpdate(topic: topic)
109+
}
110+
case .kUa: // user agent changed
111+
topic.lastSeen = LastSeen(when: Date(), ua: pres.ua)
112+
case .kRecv: // user's other session marked some messages as received
113+
assignRecv(to: topic, recv: pres.seq)
114+
case .kRead: // user's other session marked some messages as read
115+
assignRead(to: topic, read: pres.seq)
116+
case .kGone:
117+
// If topic is unknown (==nil), then we don't care to unregister it.
118+
topic.persist(false)
119+
tinode!.stopTrackingTopic(topicName: pres.src!)
120+
case .kDel: // messages deleted
121+
// Explicitly ignored: 'me' topic has no messages.
122+
break
123+
default:
124+
Tinode.log.error("ME.pres message - unknown what: %@", String(describing: pres.what))
125+
}
126+
} else {
127+
// New topic
128+
switch what {
129+
case .kAcs:
130+
let acs = Acs()
131+
acs.update(from: pres.dacs)
132+
if acs.isModeDefined {
133+
getMeta(query: getMetaGetBuilder().withSub(user: pres.src).build())
134+
} else {
135+
Tinode.log.error("ME.acs - unexpected access mode: %@", String(describing: pres.dacs))
136+
}
137+
case .kTags:
138+
// Account tags updated
139+
getMeta(query: getMetaGetBuilder().withTags().build())
140+
default:
141+
Tinode.log.error("ME.pres - topic not found: what = %@, src = %@",
142+
String(describing: pres.what), String(describing: pres.src))
143+
}
144+
}
145+
146+
if (what == MsgServerPres.What.kGone) {
147+
listener?.onSubsUpdated()
148+
}
149+
listener?.onPres(pres: pres)
150+
}
151+
152+
fileprivate func assignRecv(to topic: TopicProto, recv seq: Int?) {
153+
if (topic.recv ?? -1) < (seq ?? -1) {
154+
topic.recv = seq
155+
self.store?.setRecv(topic: topic, recv: seq!)
156+
}
157+
}
158+
159+
fileprivate func assignRead(to topic: TopicProto, read seq: Int?) {
160+
if (topic.read ?? -1) < (seq ?? -1) {
161+
topic.read = seq
162+
self.store?.setRead(topic: topic, read: topic.read!)
163+
assignRecv(to: topic, recv: topic.read)
164+
}
165+
}
166+
167+
override internal func routeMetaSub(meta: MsgServerMeta) {
168+
if let metaSubs = meta.sub as? Array<Subscription<DP, PrivateType>> {
169+
for sub in metaSubs {
170+
if let topic = tinode!.getTopic(topicName: sub.topic!) {
171+
if sub.deleted != nil {
172+
topic.persist(false)
173+
tinode!.stopTrackingTopic(topicName: sub.topic!)
174+
} else {
175+
if let t = topic as? DefaultTopic {
176+
t.update(sub: sub as! Subscription<VCard, PrivateType>)
177+
} else if let t = topic as? DefaultMeTopic {
178+
t.update(sub: sub as! Subscription<VCard, PrivateType>)
179+
} /*else if let t = topic as? DefaultFndTopic {
180+
t.update(sub: sub)
181+
} */
182+
else {
183+
Tinode.log.fault("ME.routeMetaSub - failed to update topic %@", String(describing: topic))
184+
assert(false)
185+
}
186+
}
187+
} else if sub.deleted == nil {
188+
let topic = tinode!.newTopic(sub: sub)
189+
topic.persist(true)
190+
}
191+
listener?.onMetaSub(sub: sub)
192+
}
193+
}
194+
listener?.onSubsUpdated()
195+
}
196+
}

TinodeSDK/Tinode.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,20 +286,17 @@ public class Tinode {
286286
// Queue to execute state-mutating operations on.
287287
private let operationsQueue = DispatchQueue(label: "co.tinode.operations")
288288

289-
public func baseURL(useWebsocketProtocol: Bool) -> URL? {
290-
guard !hostName.isEmpty else { return nil }
291-
return hostURL(useWebsocketProtocol: useWebsocketProtocol)?.appendingPathComponent("v\(kProtocolVersion)")
292-
}
293289
public func hostURL(useWebsocketProtocol: Bool) -> URL? {
294290
guard !hostName.isEmpty else { return nil }
295291
let protocolString = useTLS ? (useWebsocketProtocol ? "wss://" : "https://") : (useWebsocketProtocol ? "ws://" : "http://")
296292
let urlString = "\(protocolString)\(hostName)/"
297293
return URL(string: urlString)
298294
}
295+
public func baseURL(useWebsocketProtocol: Bool) -> URL? {
296+
return hostURL(useWebsocketProtocol: useWebsocketProtocol)?.appendingPathComponent("v\(kProtocolVersion)")
297+
}
299298
public func channelsURL(useWebsocketProtocol: Bool) -> URL? {
300-
guard var b = baseURL(useWebsocketProtocol: useWebsocketProtocol) else { return nil }
301-
b.appendPathComponent("/channels")
302-
return b
299+
return baseURL(useWebsocketProtocol: useWebsocketProtocol)?.appendingPathComponent("/channels")
303300
}
304301
public var isConnected: Bool {
305302
get {

0 commit comments

Comments
 (0)