-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathModel.swift
More file actions
166 lines (133 loc) · 5.39 KB
/
Model.swift
File metadata and controls
166 lines (133 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
Copyright (c) 2011-present, NimbusKit. All rights reserved.
This source code is licensed under the BSD-style license found at http://nimbuskit.info/license
*/
import Foundation
protocol ModelObjectInterface {
typealias ObjectType
func objectAtPath(path: NSIndexPath) -> ObjectType
func pathForObject(needle: ObjectType) -> NSIndexPath?
}
protocol MutableModelObjectInterface {
typealias ObjectType
mutating func addObject(object: ObjectType) -> [NSIndexPath]
mutating func addObjects(objects: [ObjectType]) -> [NSIndexPath]
mutating func addObject(object: ObjectType, toSection sectionIndex: Int) -> [NSIndexPath]
mutating func addObjects(objects: [ObjectType], toSection sectionIndex: Int) -> [NSIndexPath]
mutating func removeObjectAtIndexPath(indexPath: NSIndexPath) -> [NSIndexPath]
mutating func addSectionWithHeader(header: String) -> NSIndexSet
mutating func insertSectionWithHeader(header: String, atIndex sectionIndex: Int) -> NSIndexSet
mutating func removeSectionAtIndex(sectionIndex: Int) -> NSIndexSet
mutating func setFooterForLastSection(footer: String) -> NSIndexSet
mutating func setFooter(footer: String, atIndex sectionIndex: Int) -> NSIndexSet
}
/**
A Model is a container of objects arranged in sections with optional header and footer text.
*/
struct Model <T : AnyObject> {
typealias Section = ((header: String?, footer: String?)?, objects: [T])
var sections: [Section]
/**
Initializes the model with an array of sections.
*/
init(sections: [Section]) {
self.sections = sections
}
/**
Initializes the model with a single section containing a list of objects.
*/
init(list: [T]) {
self.init(sections: [(nil, objects: list)])
}
/**
Initializes the model with a single, object-less section.
*/
init() {
self.init(sections: [(nil, objects: [])])
}
}
extension Model : ModelObjectInterface {
/**
Returns the object at the given index path.
Providing a non-existent index path will throw an exception.
:param: path A two-index index path referencing a specific object in the receiver.
:returns: The object found at path.
*/
func objectAtPath(path: NSIndexPath) -> T {
assert(path.section < self.sections.count, "Section index out of bounds.")
assert(path.row < self.sections[path.section].objects.count, "Row index out of bounds.")
return self.sections[path.section].objects[path.row]
}
/**
Returns the index path for an object matching needle if it exists in the receiver.
This method is O(n). Please use with care.
:param: needle The object to search for in the receiver.
:returns: The index path of needle, if it was found, otherwise nil.
*/
func pathForObject(needle: T) -> NSIndexPath? {
for (sectionIndex, section) in enumerate(self.sections) {
for (objectIndex, object) in enumerate(section.objects) {
if object === needle {
return NSIndexPath(forRow: objectIndex, inSection: sectionIndex)
}
}
}
return nil
}
}
extension Model : MutableModelObjectInterface {
mutating func addObject(object: T) -> [NSIndexPath] {
self.ensureMinimalState()
return self.addObject(object, toSection: self.sections.count - 1)
}
mutating func addObjects(objects: [T]) -> [NSIndexPath] {
return objects.map(self.addObject).reduce([], combine: +)
}
mutating func addObject(object: T, toSection sectionIndex: Int) -> [NSIndexPath] {
assert(sectionIndex < self.sections.count, "Section index out of bounds.")
self.sections[sectionIndex].objects.append(object)
return [NSIndexPath(forRow: self.sections[sectionIndex].objects.count - 1, inSection: sectionIndex)]
}
mutating func addObjects(objects: [T], toSection sectionIndex: Int) -> [NSIndexPath] {
return objects.map { (var object) in self.addObject(object, toSection: sectionIndex) }
.reduce([], combine: +)
}
mutating func removeObjectAtIndexPath(indexPath: NSIndexPath) -> [NSIndexPath] {
self.sections[indexPath.section].objects.removeAtIndex(indexPath.row)
return [indexPath]
}
mutating func addSectionWithHeader(header: String) -> NSIndexSet {
self.sections.append(((header: header, nil), objects: []))
return NSIndexSet(index: self.sections.count - 1)
}
mutating func insertSectionWithHeader(header: String, atIndex sectionIndex: Int) -> NSIndexSet {
assert(sectionIndex < self.sections.count, "Section index out of bounds.")
self.sections.insert(((header: header, nil), objects: []), atIndex: sectionIndex)
return NSIndexSet(index: sectionIndex)
}
mutating func removeSectionAtIndex(sectionIndex: Int) -> NSIndexSet {
self.sections.removeAtIndex(sectionIndex)
return NSIndexSet(index: sectionIndex)
}
mutating func setFooterForLastSection(footer: String) -> NSIndexSet {
self.ensureMinimalState()
return self.setFooter(footer, atIndex: self.sections.count - 1)
}
mutating func setFooter(footer: String, atIndex sectionIndex: Int) -> NSIndexSet {
assert(sectionIndex < self.sections.count, "Section index out of bounds.")
if self.sections[sectionIndex].0 == nil {
self.sections[sectionIndex].0 = (header: nil, footer: footer)
} else {
self.sections[sectionIndex].0!.footer = footer
}
return NSIndexSet(index: sectionIndex)
}
}
// Private
extension Model {
private mutating func ensureMinimalState() {
if self.sections.count == 0 {
self.sections.append((nil, objects: []))
}
}
}