Skip to content

Commit e02871a

Browse files
committed
Add multi-message manager
1 parent 30c5ea7 commit e02871a

10 files changed

+310
-58
lines changed

SwiftMessages.xcodeproj/project.pbxproj

+27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
864495561D4F7C390056EB2A /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864495551D4F7C390056EB2A /* Identifiable.swift */; };
11+
864495591D4FA0AD0056EB2A /* Manager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864495581D4FA0AD0056EB2A /* Manager.swift */; };
12+
8644955B1D4FA2690056EB2A /* Presentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8644955A1D4FA2690056EB2A /* Presentable.swift */; };
13+
8644955D1D4FAF7C0056EB2A /* OverWindowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8644955C1D4FAF7C0056EB2A /* OverWindowViewController.swift */; };
1014
867E21531D4D01D500594A41 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 867E21521D4D01D500594A41 /* AppDelegate.swift */; };
1115
867E21551D4D01D500594A41 /* GalleryTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 867E21541D4D01D500594A41 /* GalleryTableViewController.swift */; };
1216
867E21581D4D01D500594A41 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 867E21561D4D01D500594A41 /* Main.storyboard */; };
@@ -40,6 +44,10 @@
4044
/* End PBXContainerItemProxy section */
4145

4246
/* Begin PBXFileReference section */
47+
864495551D4F7C390056EB2A /* Identifiable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Identifiable.swift; sourceTree = "<group>"; };
48+
864495581D4FA0AD0056EB2A /* Manager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Manager.swift; sourceTree = "<group>"; };
49+
8644955A1D4FA2690056EB2A /* Presentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentable.swift; sourceTree = "<group>"; };
50+
8644955C1D4FAF7C0056EB2A /* OverWindowViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverWindowViewController.swift; sourceTree = "<group>"; };
4351
867E214F1D4D01D500594A41 /* SwiftMessages.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftMessages.app; sourceTree = BUILT_PRODUCTS_DIR; };
4452
867E21521D4D01D500594A41 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
4553
867E21541D4D01D500594A41 /* GalleryTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryTableViewController.swift; sourceTree = "<group>"; };
@@ -86,6 +94,14 @@
8694
/* End PBXFrameworksBuildPhase section */
8795

8896
/* Begin PBXGroup section */
97+
864495571D4F7C490056EB2A /* Base */ = {
98+
isa = PBXGroup;
99+
children = (
100+
864495551D4F7C390056EB2A /* Identifiable.swift */,
101+
);
102+
name = Base;
103+
sourceTree = "<group>";
104+
};
89105
867E21461D4D01D500594A41 = {
90106
isa = PBXGroup;
91107
children = (
@@ -111,6 +127,7 @@
111127
children = (
112128
867E21841D4D044F00594A41 /* Configuration.swift */,
113129
867E218A1D4D3CCC00594A41 /* Message Views */,
130+
864495571D4F7C490056EB2A /* Base */,
114131
867E218E1D4D3DFD00594A41 /* Internal */,
115132
867E21811D4D021A00594A41 /* Demo */,
116133
);
@@ -169,8 +186,11 @@
169186
867E218E1D4D3DFD00594A41 /* Internal */ = {
170187
isa = PBXGroup;
171188
children = (
189+
8644955A1D4FA2690056EB2A /* Presentable.swift */,
172190
867E21931D4D50BB00594A41 /* Presenter.swift */,
191+
864495581D4FA0AD0056EB2A /* Manager.swift */,
173192
867E218F1D4D3E5E00594A41 /* UIView+Message.swift */,
193+
8644955C1D4FAF7C0056EB2A /* OverWindowViewController.swift */,
174194
);
175195
name = Internal;
176196
sourceTree = "<group>";
@@ -308,12 +328,16 @@
308328
isa = PBXSourcesBuildPhase;
309329
buildActionMask = 2147483647;
310330
files = (
331+
864495561D4F7C390056EB2A /* Identifiable.swift in Sources */,
311332
867E21831D4D025200594A41 /* MessageView.swift in Sources */,
312333
867E21551D4D01D500594A41 /* GalleryTableViewController.swift in Sources */,
334+
864495591D4FA0AD0056EB2A /* Manager.swift in Sources */,
313335
867E21851D4D044F00594A41 /* Configuration.swift in Sources */,
314336
867E21941D4D50BB00594A41 /* Presenter.swift in Sources */,
337+
8644955D1D4FAF7C0056EB2A /* OverWindowViewController.swift in Sources */,
315338
867E21531D4D01D500594A41 /* AppDelegate.swift in Sources */,
316339
867E21901D4D3E5E00594A41 /* UIView+Message.swift in Sources */,
340+
8644955B1D4FA2690056EB2A /* Presentable.swift in Sources */,
317341
);
318342
runOnlyForDeploymentPostprocessing = 0;
319343
};
@@ -538,6 +562,7 @@
538562
867E21791D4D01D500594A41 /* Release */,
539563
);
540564
defaultConfigurationIsVisible = 0;
565+
defaultConfigurationName = Release;
541566
};
542567
867E217A1D4D01D500594A41 /* Build configuration list for PBXNativeTarget "SwiftMessagesTests" */ = {
543568
isa = XCConfigurationList;
@@ -546,6 +571,7 @@
546571
867E217C1D4D01D500594A41 /* Release */,
547572
);
548573
defaultConfigurationIsVisible = 0;
574+
defaultConfigurationName = Release;
549575
};
550576
867E217D1D4D01D500594A41 /* Build configuration list for PBXNativeTarget "SwiftMessagesUITests" */ = {
551577
isa = XCConfigurationList;
@@ -554,6 +580,7 @@
554580
867E217F1D4D01D500594A41 /* Release */,
555581
);
556582
defaultConfigurationIsVisible = 0;
583+
defaultConfigurationName = Release;
557584
};
558585
/* End XCConfigurationList section */
559586
};

SwiftMessages/Configuration.swift

+20-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ public enum PresentationStyle {
2121
case Bottom
2222
}
2323

24+
public enum Duration {
25+
case Automatic
26+
case Forever
27+
case Seconds(seconds: NSTimeInterval)
28+
}
29+
2430
//public enum AnimationStyle {
2531
//
2632
// public static var defaultAnimationStyle = AnimationStyle.Default(showDuration: 0.5, hideDuration: 0.5)
@@ -98,14 +104,25 @@ public struct Configuration<V: UIView> {
98104
}
99105

100106
public func show() throws {
101-
try Presenter(configuration: self).show()
107+
let view: V
108+
if let nibName = nibName {
109+
view = try V.viewFromNib(named: nibName)
110+
} else {
111+
view = try V.viewFromNib()
112+
}
113+
show(view: view)
114+
}
115+
116+
public func show(view view: V) {
117+
let presenter = Presenter(configuration: self, view: view)
118+
globalManager.enqueue(presenter: presenter)
102119
}
103120

121+
public var duration = Duration.Automatic
122+
104123
public var nibName: String?
105124

106125
public var presentationStyle = PresentationStyle.Top
107126

108-
// public var animationStyle = AnimationStyle.defaultAnimationStyle
109-
110127
public var presentationContext = PresentationContext.InKeyWindow
111128
}

SwiftMessages/GalleryTableViewController.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,20 @@ import UIKit
1010

1111
class GalleryTableViewController: UITableViewController {
1212
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
13+
tableView.deselectRowAtIndexPath(indexPath, animated: true)
1314
let style = MessageView.errorConfiguration()
1415
let content = MessageView.contentConfiguration(title: "My Title", body: "This is my body message.")
15-
let config = Configuration<MessageView>(viewConfigurations: [style, content])
16+
var config = Configuration<MessageView>(viewConfigurations: [style, content])
17+
config.presentationContext = .OverWindow(windowLevel: UIWindowLevelStatusBar)
1618
try! config.show()
19+
let content2 = MessageView.contentConfiguration(title: "My Title 2", body: "This is my body message 2.")
20+
var config2 = Configuration<MessageView>(viewConfigurations: [style, content2])
21+
config2.presentationContext = .OverWindow(windowLevel: UIWindowLevelStatusBar)
22+
try! config2.show()
23+
let content3 = MessageView.contentConfiguration(title: "My Title", body: "This is my body message.")
24+
var config3 = Configuration<MessageView>(viewConfigurations: [style, content3])
25+
config3.presentationContext = .OverWindow(windowLevel: UIWindowLevelStatusBar)
26+
try! config3.show()
1727
}
1828
}
1929

SwiftMessages/Identifiable.swift

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Identifiable.swift
3+
// SwiftMessages
4+
//
5+
// Created by Tim Moose on 8/1/16.
6+
// Copyright © 2016 SwiftKick Mobile. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public protocol Identifiable {
12+
var identity: String { get }
13+
}

SwiftMessages/Manager.swift

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// Manager.swift
3+
// SwiftMessages
4+
//
5+
// Created by Tim Moose on 8/1/16.
6+
// Copyright © 2016 SwiftKick Mobile. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
let globalManager = Manager()
12+
13+
class Manager {
14+
15+
var queue: [Presentable] = []
16+
var current: Presentable? = nil {
17+
didSet {
18+
if oldValue != nil {
19+
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC)))
20+
dispatch_after(delayTime, dispatch_get_main_queue(), { [weak self] in
21+
guard let strongSelf = self else { return }
22+
strongSelf.dequeueNext()
23+
})
24+
}
25+
}
26+
}
27+
28+
func enqueue(presenter presenter: Presentable) {
29+
if let identity = presenter.identity {
30+
if current?.identity == identity { return }
31+
if queue.filter({ $0.identity == identity }).count > 0 { return }
32+
}
33+
queue.append(presenter)
34+
dequeueNext()
35+
}
36+
37+
func dequeueNext() {
38+
guard self.current == nil else { return }
39+
guard queue.count > 0 else { return }
40+
let current = queue.removeFirst()
41+
self.current = current
42+
do {
43+
try current.show { [weak self] completed in
44+
guard let strongSelf = self else { return }
45+
guard completed else {
46+
strongSelf.hide(presenter: current)
47+
return
48+
}
49+
if let pauseDuration = current.pauseDuration {
50+
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(pauseDuration * Double(NSEC_PER_SEC)))
51+
dispatch_after(delayTime, dispatch_get_main_queue(), {
52+
guard let strongSelf = self else { return }
53+
strongSelf.hide(presenter: current)
54+
})
55+
} else {
56+
strongSelf.hide(presenter: current)
57+
}
58+
}
59+
} catch {
60+
self.current = nil
61+
}
62+
}
63+
64+
func hide(presenter presenter: Presentable) {
65+
guard let current = current else { return }
66+
if presenter !== current { return }
67+
current.hide { [weak self] (completed) in
68+
guard let strongSelf = self else { return }
69+
guard completed else { return }
70+
strongSelf.current = nil
71+
}
72+
}
73+
}

SwiftMessages/MessageView.swift

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import UIKit
1010

11-
public class MessageView: UIView {
11+
public class MessageView: UIView, Identifiable {
1212

1313
/*
1414
MARK: - IB outlets
@@ -86,4 +86,12 @@ public class MessageView: UIView {
8686
self.button?.setImage(nil, forState: .Normal)
8787
self.button?.setTitle(nil, forState: .Normal)
8888
}
89+
90+
/*
91+
MARK: - Identifiable
92+
*/
93+
94+
public var identity: String {
95+
return "MessageView:title=\(titleLabel?.text), body=\(bodyLabel?.text)"
96+
}
8997
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// OverWindowViewController.swift
3+
// SwiftMessages
4+
//
5+
// Created by Tim Moose on 8/1/16.
6+
// Copyright © 2016 SwiftKick Mobile. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
class OverWindowViewController: UIViewController
12+
{
13+
private var window: UIWindow?
14+
15+
init(windowLevel: UIWindowLevel = UIWindowLevelNormal)
16+
{
17+
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
18+
self.window = window
19+
super.init(nibName: nil, bundle: nil)
20+
window.rootViewController = self
21+
window.windowLevel = windowLevel
22+
}
23+
24+
func install() {
25+
guard let window = window else { return }
26+
window.makeKeyAndVisible()
27+
}
28+
29+
func uninstall() {
30+
window?.hidden = true
31+
window = nil
32+
}
33+
34+
required init?(coder aDecoder: NSCoder) {
35+
fatalError("init(coder:) has not been implemented")
36+
}
37+
}

SwiftMessages/Presentable.swift

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// Presentable.swift
3+
// SwiftMessages
4+
//
5+
// Created by Tim Moose on 8/1/16.
6+
// Copyright © 2016 SwiftKick Mobile. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
protocol Presentable: class {
12+
var identity: String? { get }
13+
var pauseDuration: NSTimeInterval? { get }
14+
func show(completion completion: (completed: Bool) -> Void) throws
15+
func hide(completion completion: (completed: Bool) -> Void)
16+
}

0 commit comments

Comments
 (0)