git.iko.soy/dashared/GDproject
on master help sign in
Merge pull request #12 from dashared/developer
1aeab21Darya Rednikina
Cartfile+3−1
@@ -2,4 +2,6 @@ github "robb/Cartography"
2github "ivanbruel/MarkdownKit"
2github "ivanbruel/MarkdownKit"
3github "roberthein/TinyConstraints"
3github "roberthein/TinyConstraints"
4github "macteo/Marklight"
4github "macteo/Marklight"
5−github "ElaWorkshop/TagListView"
5+github "ElaWorkshop/TagListView"
6+github "Alamofire/Alamofire" "5.0.0-beta.2"
7+github "whitesmith/WSTagsField"
GDproject.xcodeproj/project.pbxproj+37−1
@@ -7,14 +7,22 @@
7 objects = {
7 objects = {
8
8
9/* Begin PBXBuildFile section */
9/* Begin PBXBuildFile section */
10+ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; };
11+ 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 123AD0E2223C1C3900326173 /* WSTagsField.framework */; };
12+ 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 123AD0E2223C1C3900326173 /* WSTagsField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
10 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; };
13 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; };
11 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; };
14 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; };
15+ 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; };
16+ 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; };
17+ 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */; };
12 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
19 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
13 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
20 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
14 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
21 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
15 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; };
22 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; };
16 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; };
23 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; };
17 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; };
24 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; };
25+ 1291BE3622218DBF009D3F23 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */; };
18 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D132221C321600B35452 /* ChannelListController.swift */; };
26 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D132221C321600B35452 /* ChannelListController.swift */; };
19 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D134221C42B700B35452 /* ChannelController.swift */; };
27 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D134221C42B700B35452 /* ChannelController.swift */; };
20 12D7D137221D78E800B35452 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D136221D78E800B35452 /* Channel.swift */; };
28 12D7D137221D78E800B35452 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D136221D78E800B35452 /* Channel.swift */; };
@@ -55,9 +63,11 @@
55 dstSubfolderSpec = 10;
63 dstSubfolderSpec = 10;
56 files = (
64 files = (
57 12E36DC0221441DB006FCDD7 /* Marklight.framework in Embed Frameworks */,
65 12E36DC0221441DB006FCDD7 /* Marklight.framework in Embed Frameworks */,
66+ 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */,
58 12E36DC4221441EA006FCDD7 /* Cartography.framework in Embed Frameworks */,
67 12E36DC4221441EA006FCDD7 /* Cartography.framework in Embed Frameworks */,
59 12E36DD9221594A9006FCDD7 /* TagListView.framework in Embed Frameworks */,
68 12E36DD9221594A9006FCDD7 /* TagListView.framework in Embed Frameworks */,
60 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */,
69 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */,
70+ 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */,
61 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */,
71 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */,
62 );
72 );
63 name = "Embed Frameworks";
73 name = "Embed Frameworks";
@@ -66,14 +76,20 @@
66/* End PBXCopyFilesBuildPhase section */
76/* End PBXCopyFilesBuildPhase section */
67
77
68/* Begin PBXFileReference section */
78/* Begin PBXFileReference section */
79+ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = "<group>"; };
80+ 123AD0E2223C1C3900326173 /* WSTagsField.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WSTagsField.framework; path = Carthage/Build/iOS/WSTagsField.framework; sourceTree = "<group>"; };
69 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = "<group>"; };
81 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = "<group>"; };
70 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
82 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
83+ 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
84+ 124CC7042221C2BA009DF531 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
85+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = "<group>"; };
71 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
86 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
72 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
87 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
73 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
88 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
74 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = "<group>"; };
89 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = "<group>"; };
75 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = "<group>"; };
90 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = "<group>"; };
76 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = "<group>"; };
91 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = "<group>"; };
92+ 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = "<group>"; };
77 12D7D132221C321600B35452 /* ChannelListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelListController.swift; sourceTree = "<group>"; };
93 12D7D132221C321600B35452 /* ChannelListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelListController.swift; sourceTree = "<group>"; };
78 12D7D134221C42B700B35452 /* ChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelController.swift; sourceTree = "<group>"; };
94 12D7D134221C42B700B35452 /* ChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelController.swift; sourceTree = "<group>"; };
79 12D7D136221D78E800B35452 /* Channel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channel.swift; sourceTree = "<group>"; };
95 12D7D136221D78E800B35452 /* Channel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channel.swift; sourceTree = "<group>"; };
@@ -109,9 +125,11 @@
109 buildActionMask = 2147483647;
125 buildActionMask = 2147483647;
110 files = (
126 files = (
111 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */,
127 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */,
128+ 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */,
112 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */,
129 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */,
113 12E36DD8221594A9006FCDD7 /* TagListView.framework in Frameworks */,
130 12E36DD8221594A9006FCDD7 /* TagListView.framework in Frameworks */,
114 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */,
131 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */,
132+ 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */,
115 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */,
133 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */,
116 );
134 );
117 runOnlyForDeploymentPostprocessing = 0;
135 runOnlyForDeploymentPostprocessing = 0;
@@ -140,9 +158,20 @@
140 path = "Coordinator tab bar?";
158 path = "Coordinator tab bar?";
141 sourceTree = "<group>";
159 sourceTree = "<group>";
142 };
160 };
161+ 1291BE3722218DC4009D3F23 /* Log In */ = {
162+ isa = PBXGroup;
163+ children = (
164+ 123E37A4221F1B3200F6E42A /* LogInViewController.swift */,
165+ 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */,
166+ );
167+ path = "Log In";
168+ sourceTree = "<group>";
169+ };
143 12E36D8F221424EA006FCDD7 = {
170 12E36D8F221424EA006FCDD7 = {
144 isa = PBXGroup;
171 isa = PBXGroup;
145 children = (
172 children = (
173+ 123AD0E2223C1C3900326173 /* WSTagsField.framework */,
174+ 124CC7042221C2BA009DF531 /* Alamofire.framework */,
146 12E36DD622159493006FCDD7 /* TagListView.framework */,
175 12E36DD622159493006FCDD7 /* TagListView.framework */,
147 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */,
176 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */,
148 12E36DB222143D4E006FCDD7 /* Marklight.framework */,
177 12E36DB222143D4E006FCDD7 /* Marklight.framework */,
@@ -182,6 +211,7 @@
182 12E36DBA2214401B006FCDD7 /* Simple model */ = {
211 12E36DBA2214401B006FCDD7 /* Simple model */ = {
183 isa = PBXGroup;
212 isa = PBXGroup;
184 children = (
213 children = (
214+ 124CC7022221C00C009DF531 /* Model.swift */,
185 12E36DB62214400A006FCDD7 /* Post.swift */,
215 12E36DB62214400A006FCDD7 /* Post.swift */,
186 12D7D136221D78E800B35452 /* Channel.swift */,
216 12D7D136221D78E800B35452 /* Channel.swift */,
187 12E36DB822144016006FCDD7 /* User.swift */,
217 12E36DB822144016006FCDD7 /* User.swift */,
@@ -202,8 +232,9 @@
202 12E36DCF22145923006FCDD7 /* Controller */ = {
232 12E36DCF22145923006FCDD7 /* Controller */ = {
203 isa = PBXGroup;
233 isa = PBXGroup;
204 children = (
234 children = (
235+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */,
205 1291BE32222154C3009D3F23 /* Coordinator tab bar? */,
236 1291BE32222154C3009D3F23 /* Coordinator tab bar? */,
206− 123E37A4221F1B3200F6E42A /* LogInViewController.swift */,
237+ 1291BE3722218DC4009D3F23 /* Log In */,
207 1288B5CC221F0EFE002BE6B1 /* Profile */,
238 1288B5CC221F0EFE002BE6B1 /* Profile */,
208 12E36DD322156519006FCDD7 /* News and channels */,
239 12E36DD322156519006FCDD7 /* News and channels */,
209 );
240 );
@@ -215,6 +246,7 @@
215 children = (
246 children = (
216 12D7D134221C42B700B35452 /* ChannelController.swift */,
247 12D7D134221C42B700B35452 /* ChannelController.swift */,
217 12D7D132221C321600B35452 /* ChannelListController.swift */,
248 12D7D132221C321600B35452 /* ChannelListController.swift */,
249+ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */,
218 12E36DC922144635006FCDD7 /* NewPostViewController.swift */,
250 12E36DC922144635006FCDD7 /* NewPostViewController.swift */,
219 12E36DD022148122006FCDD7 /* FullPostController.swift */,
251 12E36DD022148122006FCDD7 /* FullPostController.swift */,
220 12E36D9D221424EA006FCDD7 /* NewsController.swift */,
252 12E36D9D221424EA006FCDD7 /* NewsController.swift */,
@@ -304,9 +336,12 @@
304 isa = PBXSourcesBuildPhase;
336 isa = PBXSourcesBuildPhase;
305 buildActionMask = 2147483647;
337 buildActionMask = 2147483647;
306 files = (
338 files = (
339+ 124CC7032221C00C009DF531 /* Model.swift in Sources */,
307 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */,
340 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */,
308 12D7D137221D78E800B35452 /* Channel.swift in Sources */,
341 12D7D137221D78E800B35452 /* Channel.swift in Sources */,
342+ 1291BE3622218DBF009D3F23 /* LogInCoordinator.swift in Sources */,
309 12E36DB922144016006FCDD7 /* User.swift in Sources */,
343 12E36DB922144016006FCDD7 /* User.swift in Sources */,
344+ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */,
310 125BD5812217314A008A3575 /* NewsVC.swift in Sources */,
345 125BD5812217314A008A3575 /* NewsVC.swift in Sources */,
311 12E36DB72214400A006FCDD7 /* Post.swift in Sources */,
346 12E36DB72214400A006FCDD7 /* Post.swift in Sources */,
312 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */,
347 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */,
@@ -321,6 +356,7 @@
321 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
356 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
322 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
357 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
323 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */,
358 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */,
359+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */,
324 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */,
360 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */,
325 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */,
361 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */,
326 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */,
362 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */,
GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcschemenew file+106
@@ -0,0 +1,106 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "1010"
4+ version = "1.3">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES">
8+ <BuildActionEntries>
9+ <BuildActionEntry
10+ buildForTesting = "YES"
11+ buildForRunning = "YES"
12+ buildForProfiling = "YES"
13+ buildForArchiving = "YES"
14+ buildForAnalyzing = "YES">
15+ <BuildableReference
16+ BuildableIdentifier = "primary"
17+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
18+ BuildableName = "GDproject.app"
19+ BlueprintName = "GDproject"
20+ ReferencedContainer = "container:GDproject.xcodeproj">
21+ </BuildableReference>
22+ </BuildActionEntry>
23+ </BuildActionEntries>
24+ </BuildAction>
25+ <TestAction
26+ buildConfiguration = "Debug"
27+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+ shouldUseLaunchSchemeArgsEnv = "YES">
30+ <Testables>
31+ </Testables>
32+ <MacroExpansion>
33+ <BuildableReference
34+ BuildableIdentifier = "primary"
35+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
36+ BuildableName = "GDproject.app"
37+ BlueprintName = "GDproject"
38+ ReferencedContainer = "container:GDproject.xcodeproj">
39+ </BuildableReference>
40+ </MacroExpansion>
41+ <AdditionalOptions>
42+ </AdditionalOptions>
43+ </TestAction>
44+ <LaunchAction
45+ buildConfiguration = "Debug"
46+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48+ launchStyle = "0"
49+ useCustomWorkingDirectory = "NO"
50+ ignoresPersistentStateOnLaunch = "NO"
51+ debugDocumentVersioning = "YES"
52+ debugServiceExtension = "internal"
53+ allowLocationSimulation = "YES">
54+ <BuildableProductRunnable
55+ runnableDebuggingMode = "0">
56+ <BuildableReference
57+ BuildableIdentifier = "primary"
58+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
59+ BuildableName = "GDproject.app"
60+ BlueprintName = "GDproject"
61+ ReferencedContainer = "container:GDproject.xcodeproj">
62+ </BuildableReference>
63+ </BuildableProductRunnable>
64+ <AdditionalOptions>
65+ <AdditionalOption
66+ key = "MallocStackLogging"
67+ value = ""
68+ isEnabled = "YES">
69+ </AdditionalOption>
70+ <AdditionalOption
71+ key = "PrefersMallocStackLoggingLite"
72+ value = ""
73+ isEnabled = "YES">
74+ </AdditionalOption>
75+ <AdditionalOption
76+ key = "MallocScribble"
77+ value = ""
78+ isEnabled = "YES">
79+ </AdditionalOption>
80+ </AdditionalOptions>
81+ </LaunchAction>
82+ <ProfileAction
83+ buildConfiguration = "Release"
84+ shouldUseLaunchSchemeArgsEnv = "YES"
85+ savedToolIdentifier = ""
86+ useCustomWorkingDirectory = "NO"
87+ debugDocumentVersioning = "YES">
88+ <BuildableProductRunnable
89+ runnableDebuggingMode = "0">
90+ <BuildableReference
91+ BuildableIdentifier = "primary"
92+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
93+ BuildableName = "GDproject.app"
94+ BlueprintName = "GDproject"
95+ ReferencedContainer = "container:GDproject.xcodeproj">
96+ </BuildableReference>
97+ </BuildableProductRunnable>
98+ </ProfileAction>
99+ <AnalyzeAction
100+ buildConfiguration = "Debug">
101+ </AnalyzeAction>
102+ <ArchiveAction
103+ buildConfiguration = "Release"
104+ revealArchiveInOrganizer = "YES">
105+ </ArchiveAction>
106+</Scheme>
GDproject/Base.lproj/Main.storyboard+79−14
@@ -74,8 +74,8 @@
74 <nil key="highlightedColor"/>
74 <nil key="highlightedColor"/>
75 </label>
75 </label>
76 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yir-N6-Q6f">
76 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yir-N6-Q6f">
77− <rect key="frame" x="297" y="0.0" width="46" height="30"/>
77+ <rect key="frame" x="246" y="0.0" width="97" height="30"/>
78− <state key="normal" title="Button"/>
78+ <state key="normal" title="New message"/>
79 </button>
79 </button>
80 </subviews>
80 </subviews>
81 </stackView>
81 </stackView>
@@ -214,6 +214,50 @@
214 </objects>
214 </objects>
215 <point key="canvasLocation" x="2803" y="1756"/>
215 <point key="canvasLocation" x="2803" y="1756"/>
216 </scene>
216 </scene>
217+ <!--Simplified Channels List-->
218+ <scene sceneID="gAa-jy-vpG">
219+ <objects>
220+ <tableViewController storyboardIdentifier="SimplifiedChannelsList" id="pgD-Fz-bkD" customClass="SimplifiedChannelsList" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
221+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="Onz-Ne-JgO">
222+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
223+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
224+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
225+ <prototypes>
226+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="cell" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell1" textLabel="mZB-IA-VFq" detailTextLabel="6A8-yj-Eqe" style="IBUITableViewCellStyleSubtitle" id="6dT-km-gZC">
227+ <rect key="frame" x="0.0" y="28" width="375" height="44"/>
228+ <autoresizingMask key="autoresizingMask"/>
229+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="6dT-km-gZC" id="h7U-va-4PX">
230+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
231+ <autoresizingMask key="autoresizingMask"/>
232+ <subviews>
233+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mZB-IA-VFq">
234+ <rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
235+ <autoresizingMask key="autoresizingMask"/>
236+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
237+ <nil key="textColor"/>
238+ <nil key="highlightedColor"/>
239+ </label>
240+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6A8-yj-Eqe">
241+ <rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
242+ <autoresizingMask key="autoresizingMask"/>
243+ <fontDescription key="fontDescription" type="system" pointSize="12"/>
244+ <nil key="textColor"/>
245+ <nil key="highlightedColor"/>
246+ </label>
247+ </subviews>
248+ </tableViewCellContentView>
249+ </tableViewCell>
250+ </prototypes>
251+ <connections>
252+ <outlet property="dataSource" destination="pgD-Fz-bkD" id="V4y-ql-GOS"/>
253+ <outlet property="delegate" destination="pgD-Fz-bkD" id="Avz-4G-8j6"/>
254+ </connections>
255+ </tableView>
256+ </tableViewController>
257+ <placeholder placeholderIdentifier="IBFirstResponder" id="731-nV-bC3" userLabel="First Responder" sceneMemberID="firstResponder"/>
258+ </objects>
259+ <point key="canvasLocation" x="4372" y="-598"/>
260+ </scene>
217 <!--Channel List Controller-->
261 <!--Channel List Controller-->
218 <scene sceneID="4eb-Jz-gcx">
262 <scene sceneID="4eb-Jz-gcx">
219 <objects>
263 <objects>
@@ -320,23 +364,34 @@
320 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
364 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
321 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
365 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
322 <subviews>
366 <subviews>
323− <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sSz-uJ-A7n">
367+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aRT-Du-PVE">
324− <rect key="frame" x="0.0" y="20" width="375" height="647"/>
368+ <rect key="frame" x="0.0" y="55" width="375" height="612"/>
325 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
369 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
326 </view>
370 </view>
371+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="368-bf-QU6">
372+ <rect key="frame" x="0.0" y="20" width="375" height="35"/>
373+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
374+ <constraints>
375+ <constraint firstAttribute="height" constant="35" id="5ER-su-0Yf"/>
376+ </constraints>
377+ </view>
327 </subviews>
378 </subviews>
328 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
379 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
329 <constraints>
380 <constraints>
381+ <constraint firstItem="wxD-Ek-mgv" firstAttribute="trailing" secondItem="aRT-Du-PVE" secondAttribute="trailing" id="2ee-e8-G7A"/>
330− <constraint firstItem="sSz-uJ-A7n" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="2J7-Bi-PzG"/>
382+ <constraint firstItem="368-bf-QU6" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="9Rl-wh-kZf"/>
331− <constraint firstItem="wxD-Ek-mgv" firstAttribute="trailing" secondItem="sSz-uJ-A7n" secondAttribute="trailing" id="HrU-bs-qsE"/>
383+ <constraint firstItem="aRT-Du-PVE" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="UBy-Ch-zSL"/>
332− <constraint firstItem="wxD-Ek-mgv" firstAttribute="bottom" secondItem="sSz-uJ-A7n" secondAttribute="bottom" id="oZ7-xQ-9Cb"/>
384+ <constraint firstItem="aRT-Du-PVE" firstAttribute="top" secondItem="368-bf-QU6" secondAttribute="bottom" id="WaE-kh-6w3"/>
333− <constraint firstItem="sSz-uJ-A7n" firstAttribute="top" secondItem="wxD-Ek-mgv" secondAttribute="top" id="zuN-mZ-hgU"/>
385+ <constraint firstItem="368-bf-QU6" firstAttribute="top" secondItem="wxD-Ek-mgv" secondAttribute="top" id="aI9-JQ-7dq"/>
386+ <constraint firstItem="368-bf-QU6" firstAttribute="trailing" secondItem="wxD-Ek-mgv" secondAttribute="trailing" id="haz-0r-Cud"/>
387+ <constraint firstItem="wxD-Ek-mgv" firstAttribute="bottom" secondItem="aRT-Du-PVE" secondAttribute="bottom" id="x3f-eb-ACt"/>
334 </constraints>
388 </constraints>
335 <viewLayoutGuide key="safeArea" id="wxD-Ek-mgv"/>
389 <viewLayoutGuide key="safeArea" id="wxD-Ek-mgv"/>
336 </view>
390 </view>
337 <connections>
391 <connections>
338− <outlet property="bottomTextViewConstraint" destination="oZ7-xQ-9Cb" id="6In-71-0TX"/>
392+ <outlet property="bottomTextViewConstraint" destination="x3f-eb-ACt" id="ktT-im-InH"/>
339− <outlet property="view1" destination="sSz-uJ-A7n" id="5nX-oT-kUi"/>
393+ <outlet property="view1" destination="aRT-Du-PVE" id="m3d-kW-vqW"/>
394+ <outlet property="viewForTags" destination="368-bf-QU6" id="M5s-RZ-Fq7"/>
340 </connections>
395 </connections>
341 </viewController>
396 </viewController>
342 <placeholder placeholderIdentifier="IBFirstResponder" id="3fd-7N-vPg" userLabel="First Responder" sceneMemberID="firstResponder"/>
397 <placeholder placeholderIdentifier="IBFirstResponder" id="3fd-7N-vPg" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -387,16 +442,22 @@
387 </textField>
442 </textField>
388 </subviews>
443 </subviews>
389 </stackView>
444 </stackView>
445+ <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="K3b-gv-JPp">
446+ <rect key="frame" x="177.5" y="333.5" width="20" height="20"/>
447+ </activityIndicatorView>
390 </subviews>
448 </subviews>
391 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
449 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
392 <constraints>
450 <constraints>
393 <constraint firstItem="UwC-m5-jA5" firstAttribute="leading" secondItem="LOR-Yw-0FU" secondAttribute="leading" constant="16" id="M6z-nU-H40"/>
451 <constraint firstItem="UwC-m5-jA5" firstAttribute="leading" secondItem="LOR-Yw-0FU" secondAttribute="leading" constant="16" id="M6z-nU-H40"/>
452+ <constraint firstItem="K3b-gv-JPp" firstAttribute="centerY" secondItem="LOR-Yw-0FU" secondAttribute="centerY" id="eOp-yQ-n28"/>
453+ <constraint firstItem="K3b-gv-JPp" firstAttribute="centerX" secondItem="LOR-Yw-0FU" secondAttribute="centerX" id="gdW-VU-pE8"/>
394 <constraint firstItem="LOR-Yw-0FU" firstAttribute="trailing" secondItem="UwC-m5-jA5" secondAttribute="trailing" constant="16" id="jST-rr-MMX"/>
454 <constraint firstItem="LOR-Yw-0FU" firstAttribute="trailing" secondItem="UwC-m5-jA5" secondAttribute="trailing" constant="16" id="jST-rr-MMX"/>
395 <constraint firstItem="UwC-m5-jA5" firstAttribute="top" secondItem="LOR-Yw-0FU" secondAttribute="top" constant="80" id="rvy-Ii-bwS"/>
455 <constraint firstItem="UwC-m5-jA5" firstAttribute="top" secondItem="LOR-Yw-0FU" secondAttribute="top" constant="80" id="rvy-Ii-bwS"/>
396 </constraints>
456 </constraints>
397 <viewLayoutGuide key="safeArea" id="LOR-Yw-0FU"/>
457 <viewLayoutGuide key="safeArea" id="LOR-Yw-0FU"/>
398 </view>
458 </view>
399 <connections>
459 <connections>
460+ <outlet property="indicatorView" destination="K3b-gv-JPp" id="mSR-QU-VZQ"/>
400 <outlet property="mailTextField" destination="mFP-WG-jY5" id="7SN-qe-DDm"/>
461 <outlet property="mailTextField" destination="mFP-WG-jY5" id="7SN-qe-DDm"/>
401 </connections>
462 </connections>
402 </viewController>
463 </viewController>
@@ -408,7 +469,7 @@
408 <scene sceneID="RSd-UJ-34B">
469 <scene sceneID="RSd-UJ-34B">
409 <objects>
470 <objects>
410 <navigationController storyboardIdentifier="navChannels" automaticallyAdjustsScrollViewInsets="NO" id="NuV-ZG-ld2" sceneMemberID="viewController">
471 <navigationController storyboardIdentifier="navChannels" automaticallyAdjustsScrollViewInsets="NO" id="NuV-ZG-ld2" sceneMemberID="viewController">
411− <tabBarItem key="tabBarItem" title="Posts" id="dH2-sL-YsR"/>
472+ <tabBarItem key="tabBarItem" title="Posts" image="5" selectedImage="5" id="dH2-sL-YsR"/>
412 <toolbarItems/>
473 <toolbarItems/>
413 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="6qB-3A-tuL">
474 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="6qB-3A-tuL">
414 <rect key="frame" x="0.0" y="20" width="375" height="96"/>
475 <rect key="frame" x="0.0" y="20" width="375" height="96"/>
@@ -437,7 +498,7 @@
437 <outlet property="delegate" destination="Nqc-TI-ueQ" id="kTM-Be-8qb"/>
498 <outlet property="delegate" destination="Nqc-TI-ueQ" id="kTM-Be-8qb"/>
438 </connections>
499 </connections>
439 </tableView>
500 </tableView>
440− <navigationItem key="navigationItem" id="b8E-6f-hLM"/>
501+ <navigationItem key="navigationItem" largeTitleDisplayMode="always" id="b8E-6f-hLM"/>
441 </tableViewController>
502 </tableViewController>
442 <placeholder placeholderIdentifier="IBFirstResponder" id="c9d-Kh-itI" userLabel="First Responder" sceneMemberID="firstResponder"/>
503 <placeholder placeholderIdentifier="IBFirstResponder" id="c9d-Kh-itI" userLabel="First Responder" sceneMemberID="firstResponder"/>
443 </objects>
504 </objects>
@@ -447,7 +508,7 @@
447 <scene sceneID="dJj-Hp-Wa9">
508 <scene sceneID="dJj-Hp-Wa9">
448 <objects>
509 <objects>
449 <navigationController storyboardIdentifier="navPosts" automaticallyAdjustsScrollViewInsets="NO" id="tae-5q-wvT" sceneMemberID="viewController">
510 <navigationController storyboardIdentifier="navPosts" automaticallyAdjustsScrollViewInsets="NO" id="tae-5q-wvT" sceneMemberID="viewController">
450− <tabBarItem key="tabBarItem" title="Profile" id="lYy-LQ-AcR"/>
511+ <tabBarItem key="tabBarItem" title="Profile" image="1" selectedImage="1" id="lYy-LQ-AcR"/>
451 <toolbarItems/>
512 <toolbarItems/>
452 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="q3a-ph-c8V">
513 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="q3a-ph-c8V">
453 <rect key="frame" x="0.0" y="20" width="375" height="96"/>
514 <rect key="frame" x="0.0" y="20" width="375" height="96"/>
@@ -465,7 +526,7 @@
465 <!--Navigation Controller-->
526 <!--Navigation Controller-->
466 <scene sceneID="r0M-xH-UwU">
527 <scene sceneID="r0M-xH-UwU">
467 <objects>
528 <objects>
468− <navigationController id="609-2j-gc9" sceneMemberID="viewController">
529+ <navigationController storyboardIdentifier="root" id="609-2j-gc9" sceneMemberID="viewController">
469 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Ah8-NU-AsD">
530 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Ah8-NU-AsD">
470 <rect key="frame" x="0.0" y="20" width="375" height="44"/>
531 <rect key="frame" x="0.0" y="20" width="375" height="44"/>
471 <autoresizingMask key="autoresizingMask"/>
532 <autoresizingMask key="autoresizingMask"/>
@@ -495,4 +556,8 @@
495 <point key="canvasLocation" x="-466" y="641"/>
556 <point key="canvasLocation" x="-466" y="641"/>
496 </scene>
557 </scene>
497 </scenes>
558 </scenes>
559+ <resources>
560+ <image name="1" width="30" height="30"/>
561+ <image name="5" width="30" height="30"/>
562+ </resources>
498</document>
563</document>
GDproject/Constants.swift+5
@@ -41,3 +41,8 @@ let newsController = "NewsController"
41
41
42/// constant for switching to view controller for log in app
42/// constant for switching to view controller for log in app
43let logInController = "LogInController"
43let logInController = "LogInController"
44+
45+
46+let profileViewController = "ProfileViewController"
47+
48+let simplifiedChannelsList = "SimplifiedChannelsList"
GDproject/Controller/ News and channels/ChannelController.swift+139−58
@@ -9,7 +9,12 @@
9import UIKit
9import UIKit
10
10
11protocol DataDelegate {
11protocol DataDelegate {
12− func passData(for row: Int, channel: Channel)
12+ func passData(for row: Int, channel: Model.Channels)
13+}
14+
15+enum ActiveTable {
16+ case tags
17+ case people
13}
18}
14
19
15class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UISearchBarDelegate
20class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UISearchBarDelegate
@@ -17,14 +22,16 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
17 @IBOutlet weak var tableView: UITableView!
22 @IBOutlet weak var tableView: UITableView!
18
23
19 var index: Int?
24 var index: Int?
20− var channel: Channel?
25+ var channel: Model.Channels?
21 var myProtocol: DataDelegate?
26 var myProtocol: DataDelegate?
22
27
23− var dataSource: [String] = []
28+ var fullTags: [String] = Array(Model.Channels.fullTags)
29+
30+ var dataSourcePeople: [Model.Users] = []
24
31
25− var fullPeople: [String] = ["Other Ann", "Other Ilya", "Other Kostya", "Pasha", "Valera"]
32+ var dataSourceTags: [String] = []
26
33
27− var fullTags: [String] = ["# happy", "# sad", "# meme", "# shershakov", "# pupsik"]
34+ var activeDataSource: ActiveTable = .people
28
35
29 @IBOutlet weak var viewww: UIView!
36 @IBOutlet weak var viewww: UIView!
30
37
@@ -32,23 +39,58 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
32
39
33 override func viewDidLoad() {
40 override func viewDidLoad() {
34 super.viewDidLoad()
41 super.viewDidLoad()
42+
43+ dataSourceTags = channel!.tags
44+ setUpController()
45+ tableView.reloadData()
46+ }
47+
48+ var reloadtable: Bool = false {
49+ didSet{
50+ tableView.reloadData()
51+ }
52+ }
53+
54+ override func viewWillAppear(_ animated: Bool) {
55+ super.viewWillAppear(animated)
56+ Model.getUsers(for: channel!.people) { [weak self] (people) in
57+ self?.dataSourcePeople = Array(people.values)
58+ self?.reloadtable = true
59+ }
60+ }
61+
62+ // TODO: update channel
63+ override func viewWillDisappear(_ animated: Bool) {
64+ super.viewWillDisappear(animated)
65+ if let _ = channel?.id { print("update")
66+ Model.updateChannel(with: channel!)
67+ } else { print("create")
68+ Model.createChannel(with: channel!)
69+ }
70+ }
71+
72+ func setUpController(){
73+
35 tableView.delegate = self
74 tableView.delegate = self
36 tableView.dataSource = self
75 tableView.dataSource = self
37− dataSource = channel!.people
38 tableView.register(UITableViewCell.self, forCellReuseIdentifier: itemCellId)
76 tableView.register(UITableViewCell.self, forCellReuseIdentifier: itemCellId)
39 tableView.reloadData()
77 tableView.reloadData()
40 textField.delegate = self
78 textField.delegate = self
41 searchBar.delegate = self
79 searchBar.delegate = self
42 searchBar.selectedScopeButtonIndex = 0
80 searchBar.selectedScopeButtonIndex = 0
81+
43− navigationItem.title = channel?.title
82+ navigationItem.title = channel?.name
44− textField.placeholder = channel?.title
83+ textField.placeholder = channel?.name
84+
45 textField.addTarget(self, action: #selector(changedText(_:)), for: .editingChanged)
85 textField.addTarget(self, action: #selector(changedText(_:)), for: .editingChanged)
46 }
86 }
47
87
88+
89+
48 @objc func changedText(_ textField: UITextField){
90 @objc func changedText(_ textField: UITextField){
49 navigationItem.title = textField.text
91 navigationItem.title = textField.text
50− channel?.title = textField.text!
92+ channel?.name = textField.text!
51− myProtocol?.passData(for: index!, channel: channel!)
93+ // myProtocol?.passData(for: index!, channel: channel!)
52 }
94 }
53
95
54 func numberOfSections(in tableView: UITableView) -> Int {
96 func numberOfSections(in tableView: UITableView) -> Int {
@@ -56,72 +98,76 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
56 }
98 }
57
99
58 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
100 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
101+ switch activeDataSource {
102+ case .people:
59− return dataSource.count
103+ return dataSourcePeople.count
104+ case .tags:
105+ return dataSourceTags.count
106+ }
60 }
107 }
61
108
62 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
109 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
63− if searchBar.isFirstResponder {
64− if let cell = tableView.cellForRow(at: indexPath)
110+ if searchBar.isFirstResponder, let cell = tableView.cellForRow(at: indexPath)
65− {
111+ {
66−
67 if cell.accessoryType == .none{
112 if cell.accessoryType == .none{
68 switch searchBar.selectedScopeButtonIndex{
113 switch searchBar.selectedScopeButtonIndex{
69 case 0:
114 case 0:
70− channel?.people.append(dataSource[indexPath.row])
115+ channel?.people.append(dataSourcePeople[indexPath.row].id)
71 print("people = \(channel!.people)")
116 print("people = \(channel!.people)")
72 case 1:
117 case 1:
73− channel?.hashtags.append(dataSource[indexPath.row])
118+ channel?.tags.append(dataSourceTags[indexPath.row])
74− print("hashtags = \(channel!.hashtags)")
119+ print("hashtags = \(channel!.tags)")
75 default:
120 default:
76 break
121 break
77 }
122 }
78−
79 cell.accessoryType = .checkmark
123 cell.accessoryType = .checkmark
80 } else {
124 } else {
81 cell.accessoryType = .none
125 cell.accessoryType = .none
82
126
83 switch searchBar.selectedScopeButtonIndex{
127 switch searchBar.selectedScopeButtonIndex{
84 case 0:
128 case 0:
85− channel?.people = channel!.people.filter({ (s) -> Bool in
86− !fullPeople[indexPath.row].contains(s)
129+ let filtered = channel!.people.filter{ $0 != Model.Channels.fullPeople[indexPath.row].id }
87− })
130+ channel?.people = filtered
88− // TODO: dataPass
89 case 1:
131 case 1:
90− channel?.hashtags = channel!.hashtags.filter({ (s) -> Bool in
132+ let filtered = channel!.tags.filter{
91− !fullTags[indexPath.row].contains(s)
133+ !fullTags[indexPath.row].contains($0)
92− })
134+ }
93− // TODO: dataPass
135+ channel?.tags = filtered
94 default:
136 default:
95 break
137 break
96 }
138 }
97 }
139 }
98− }
140+ }
99− } else
141+ else
100 {
142 {
101− tableView.beginUpdates()
143+ switch activeDataSource{
102− dataSource.remove(at: indexPath.row)
103− switch searchBar.selectedScopeButtonIndex{
104− case 0:
144+ case .people:
105 channel?.people.remove(at: indexPath.row)
145 channel?.people.remove(at: indexPath.row)
106− // TODO: dataPass
107− case 1:
146+ case .tags:
108− channel?.hashtags.remove(at: indexPath.row)
147+ channel?.tags.remove(at: indexPath.row)
109− // TODO: dataPass
148+ }
110− default:
149+
111− break
150+ tableView.beginUpdates()
151+ switch activeDataSource{
152+ case .people:
153+ dataSourcePeople.remove(at: indexPath.row)
154+ case .tags:
155+ dataSourceTags.remove(at: indexPath.row)
112 }
156 }
113 tableView.deleteRows(at: [indexPath], with: .none)
157 tableView.deleteRows(at: [indexPath], with: .none)
114 tableView.endUpdates()
158 tableView.endUpdates()
115 }
159 }
116 }
160 }
117
161
118− func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
162+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
163+ {
119 let cell = tableView.dequeueReusableCell(withIdentifier: itemCellId, for: indexPath)
164 let cell = tableView.dequeueReusableCell(withIdentifier: itemCellId, for: indexPath)
120
165
121− cell.textLabel?.text = dataSource[indexPath.row]
166+ cell.textLabel?.text = activeDataSource == .people ? "\(dataSourcePeople[indexPath.row].firstName) \(dataSourcePeople[indexPath.row].middleName) \(dataSourcePeople[indexPath.row].lastName)" : dataSourceTags[indexPath.row]
122 cell.selectionStyle = .none
167 cell.selectionStyle = .none
123
168
169+ if activeDataSource == .tags && channel!.tags.contains(dataSourceTags[indexPath.row]) ||
124− if channel!.hashtags.contains(dataSource[indexPath.row]) || channel!.people.contains(dataSource[indexPath.row])
170+ activeDataSource == .people && channel!.people.contains(dataSourcePeople[indexPath.row].id)
125 {
171 {
126 cell.accessoryType = .checkmark
172 cell.accessoryType = .checkmark
127 } else {
173 } else {
@@ -147,7 +193,7 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
147 {
193 {
148 let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 70))
194 let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 70))
149
195
150− if flag {
196+ if activeDataSource == .tags {
151 searchBar.selectedScopeButtonIndex = 1
197 searchBar.selectedScopeButtonIndex = 1
152 } else {
198 } else {
153 searchBar.selectedScopeButtonIndex = 0
199 searchBar.selectedScopeButtonIndex = 0
@@ -165,7 +211,12 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
165
211
166 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
212 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
167 if searchText.isEmpty {
213 if searchText.isEmpty {
168− dataSource = searchBar.selectedScopeButtonIndex == 0 ? fullPeople : fullTags
214+ switch activeDataSource {
215+ case .people:
216+ dataSourcePeople = Model.Channels.fullPeople
217+ case .tags:
218+ dataSourceTags = fullTags
219+ }
169 tableView.reloadData()
220 tableView.reloadData()
170 } else {
221 } else {
171 filterTableView(ind: searchBar.selectedScopeButtonIndex, text: searchText)
222 filterTableView(ind: searchBar.selectedScopeButtonIndex, text: searchText)
@@ -175,12 +226,13 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
175 func filterTableView(ind: Int, text: String){
226 func filterTableView(ind: Int, text: String){
176 switch ind {
227 switch ind {
177 case 0:
228 case 0:
178− dataSource = fullPeople.filter({ (s) -> Bool in
229+ dataSourcePeople = Model.Channels.fullPeople.filter {
230+ let fullName = "\($0.firstName) \($0.middleName) \($0.lastName)"
179− s.lowercased().contains(text.lowercased())
231+ return fullName.lowercased().contains(text.lowercased())
180− })
232+ }
181 tableView.reloadData()
233 tableView.reloadData()
182 case 1:
234 case 1:
183− dataSource = fullTags.filter({ (s) -> Bool in
235+ dataSourceTags = fullTags.filter({ (s) -> Bool in
184 s.lowercased().contains(text.lowercased())
236 s.lowercased().contains(text.lowercased())
185 })
237 })
186 tableView.reloadData()
238 tableView.reloadData()
@@ -189,33 +241,62 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
189 }
241 }
190 }
242 }
191
243
192− var flag = false
193−
194 func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
244 func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
245+ activeDataSource = searchBar.selectedScopeButtonIndex == 0 ? .people : .tags
195 if searchBar.isFirstResponder {
246 if searchBar.isFirstResponder {
196− dataSource = searchBar.selectedScopeButtonIndex == 0 ? fullPeople : fullTags
247+ switch activeDataSource{
197− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
248+ case .people :
249+ dataSourcePeople = Model.Channels.fullPeople
250+ dataSourceTags = []
251+ case .tags:
252+ dataSourceTags = fullTags
253+ dataSourcePeople = []
254+ }
198 if (!(searchBar.text?.isEmpty ?? true))
255 if (!(searchBar.text?.isEmpty ?? true))
199 {
256 {
200 filterTableView(ind: selectedScope, text: searchBar.text!)
257 filterTableView(ind: selectedScope, text: searchBar.text!)
201 }
258 }
202 tableView.reloadData()
259 tableView.reloadData()
203 } else if !(searchBar.isFirstResponder){
260 } else if !(searchBar.isFirstResponder){
204− dataSource = searchBar.selectedScopeButtonIndex == 0 ? channel!.people : channel!.hashtags
261+ switch activeDataSource{
205− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
262+ case .people :
263+ dataSourcePeople = channel!.people.map({ (item) -> Model.Users in
264+ Model.Channels.fullPeopleDict[item]!
265+ })
266+ dataSourceTags = []
267+ case .tags:
268+ dataSourceTags = channel!.tags
269+ dataSourcePeople = []
270+ }
206 tableView.reloadData()
271 tableView.reloadData()
207 }
272 }
208 }
273 }
209
274
210 func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
275 func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
211− dataSource = searchBar.selectedScopeButtonIndex == 0 ? channel!.people : channel!.hashtags
276+ switch activeDataSource{
277+ case .people :
278+ dataSourcePeople = channel!.people.map({ (item) -> Model.Users in
279+ Model.Channels.fullPeopleDict[item]!
280+ })
281+ dataSourceTags = []
282+ case .tags:
283+ dataSourceTags = channel!.tags
284+ dataSourcePeople = []
285+ }
212− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
286+ activeDataSource = searchBar.selectedScopeButtonIndex == 0 ? .people : .tags
213 tableView.reloadData()
287 tableView.reloadData()
214 }
288 }
215
289
216 func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
290 func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
217− dataSource = searchBar.selectedScopeButtonIndex == 0 ? fullPeople : fullTags
291+ switch activeDataSource{
292+ case .people :
293+ dataSourcePeople = Model.Channels.fullPeople
294+ dataSourceTags = []
295+ case .tags:
296+ dataSourceTags = fullTags
297+ dataSourcePeople = []
298+ }
218− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
299+ activeDataSource = searchBar.selectedScopeButtonIndex == 0 ? .people : .tags
219 tableView.reloadData()
300 tableView.reloadData()
220 }
301 }
221
302
GDproject/Controller/ News and channels/ChannelListController.swift+54−41
@@ -17,13 +17,19 @@ struct ChannelData{
17class ChannelListController: UITableViewController, DataDelegate {
17class ChannelListController: UITableViewController, DataDelegate {
18
18
19 // MARK: - Output -
19 // MARK: - Output -
20− var onChannelSelected: ((Channel) -> Void)?
20+ var onChannelSelected: ((Model.Channels) -> Void)?
21
21
22− // MARK:- filter search controller
22+ // MARK: - filter search controller
23− var filteredDataSource = [Channel]()
23+ var filteredDataSource = [Model.Channels]()
24
24
25 var myProtocol: DataDelegate?
25 var myProtocol: DataDelegate?
26− var displayingChannel: Channel?
26+ var displayingChannel: Model.Channels?
27+
28+ var toReload: Bool = false {
29+ didSet{
30+ tableView.reloadData()
31+ }
32+ }
27
33
28 var isFiltering: Bool {
34 var isFiltering: Bool {
29 return searchController.isActive && !searchBarIsEmpty()
35 return searchController.isActive && !searchBarIsEmpty()
@@ -35,21 +41,24 @@ class ChannelListController: UITableViewController, DataDelegate {
35
41
36 func filterContentForSearchText(_ searchText: String, scope: String = "All")
42 func filterContentForSearchText(_ searchText: String, scope: String = "All")
37 {
43 {
38− filteredDataSource = ChannelListController.dataSource.filter({(keys : Channel) -> Bool in
44+ filteredDataSource = dataSource.filter({(keys : Model.Channels) -> Bool in
39− return keys.title.lowercased().contains(searchText.lowercased())
45+ return keys.name.lowercased().contains(searchText.lowercased())
40 })
46 })
41
47
42 tableView.reloadData()
48 tableView.reloadData()
43 }
49 }
44
50
45− func passData(for row: Int, channel: Channel) {
51+ func passData(for row: Int, channel: Model.Channels) {
46− ChannelListController.dataSource[row] = channel
52+ // dataSource[row] = channel
47 }
53 }
48
54
49 var searchController = UISearchController(searchResultsController: nil)
55 var searchController = UISearchController(searchResultsController: nil)
50
56
51 override func viewDidLoad() {
57 override func viewDidLoad() {
52 super.viewDidLoad()
58 super.viewDidLoad()
59+
60+ Model.usersAllGet()
61+
53 setUpNavigationBar()
62 setUpNavigationBar()
54 navigationItem.title = "Channels"
63 navigationItem.title = "Channels"
55 searchController.searchResultsUpdater = self
64 searchController.searchResultsUpdater = self
@@ -60,49 +69,48 @@ class ChannelListController: UITableViewController, DataDelegate {
60 }
69 }
61
70
62 func setUpNavigationBar(){
71 func setUpNavigationBar(){
63− navigationController?.navigationItem.largeTitleDisplayMode = .always
72+ navigationItem.largeTitleDisplayMode = .always
64 navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addChannel))]
73 navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addChannel))]
65 }
74 }
66
75
67− override func viewWillAppear(_ animated: Bool)
76+ override func viewDidAppear(_ animated: Bool)
68 {
77 {
69− super.viewWillAppear(animated)
78+ super.viewDidAppear(animated)
70 searchController.isActive = false
79 searchController.isActive = false
71− tableView.reloadData()
80+ askForUpdates()
81+ }
82+
83+ private func askForUpdates(){
84+ Model.channelsList { [weak self] (channels) in
85+ self?.dataSource = [ChannelListController.generalChannel] + channels
86+ self?.toReload = true
87+ }
88+ }
89+
90+ override func viewWillAppear(_ animated: Bool) {
91+ super.viewWillAppear(true)
92+ askForUpdates()
72 }
93 }
73
94
74 @objc func addChannel()
95 @objc func addChannel()
75 {
96 {
76− // insertion
77− tableView.beginUpdates()
78− ChannelListController.dataSource.insert(Channel(title: "Untitled", subtitle: "No", hashtags: [], people: [], posts: []), at: 1)
79− tableView.insertRows(at: [IndexPath(row: 1, section: 0)], with: .none)
80− tableView.endUpdates()
81−
82 // editing mode is on automatically
97 // editing mode is on automatically
83 let vc = storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController
98 let vc = storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController
84 vc.index = 1
99 vc.index = 1
85 vc.myProtocol = self
100 vc.myProtocol = self
86− vc.channel = ChannelListController.dataSource[1]
101+ vc.channel = Model.Channels(people: [], name: "Untitled", tags: [])
87 navigationController?.pushViewController(vc, animated: true)
102 navigationController?.pushViewController(vc, animated: true)
88 }
103 }
89
104
90− static var dataSource : [Channel] = [
105+ static let generalChannel = Model.Channels(people: [], name: "General", id: -1, tags: [])
91− Channel(title: "General", subtitle: "All posts", hashtags: ["All"], people: ["All"], posts: []),
106+
92− Channel(title: "Title", subtitle: "subtitle", hashtags: ["# sad", "# happy"], people: ["Seva", "Andrey"], posts: [Post(dataArray: [.text("Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard.")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05")]),
107+ var dataSource : [Model.Channels] = [ ChannelListController.generalChannel ]
93− Channel(title: "Title2", subtitle: "subtitle2", hashtags: ["# studyhard", "# university"], people: ["Pasha", "Olya", "Andrey", "Ilya"], posts:
94− [
95− Post(dataArray: [.text("L'avantage du Lorem Ipsum sur un texte générique comme 'Du texte. Du texte. Du texte.' est qu'il possède une distribution de lettres plus ou moins normale.")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05"),
96− Post(dataArray: [.text("par accident, souvent intentionnellement (histoire d'y rajouter de petits clins d'oeil, voire des phrases embarassantes).")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05")
97− ]),
98− Channel(title: "Title3", subtitle: "subtitle3", hashtags: ["# lol", "# meme", "# hehe"], people: ["Superman"], posts: [Post(dataArray: [.text("Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié.")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05")])
99− ]
100
108
101 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
109 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
102 if isFiltering {
110 if isFiltering {
103 return filteredDataSource.count
111 return filteredDataSource.count
104 } else {
112 } else {
105− return ChannelListController.dataSource.count
113+ return dataSource.count
106 }
114 }
107 }
115 }
108
116
@@ -113,11 +121,11 @@ class ChannelListController: UITableViewController, DataDelegate {
113
121
114
122
115 if isFiltering{
123 if isFiltering{
116− cell.textLabel?.text = filteredDataSource[indexPath.row].title
124+ cell.textLabel?.text = filteredDataSource[indexPath.row].name
117− cell.detailTextLabel?.text = filteredDataSource[indexPath.row].subtitle
125+ cell.detailTextLabel?.text = filteredDataSource[indexPath.row].tags.reduce(String(), { (r, next) -> String in "\(r) \(next)" })
118 } else {
126 } else {
119− cell.textLabel?.text = ChannelListController.dataSource[indexPath.row].title
127+ cell.textLabel?.text = dataSource[indexPath.row].name
120− cell.detailTextLabel?.text = ChannelListController.dataSource[indexPath.row].subtitle
128+ cell.detailTextLabel?.text = dataSource[indexPath.row].tags.reduce(String(), { (r, next) -> String in "\(r) \(next)" })
121 }
129 }
122
130
123 return cell
131 return cell
@@ -140,24 +148,29 @@ class ChannelListController: UITableViewController, DataDelegate {
140 let vc = self?.storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController
148 let vc = self?.storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController
141 vc.index = indexPath.row
149 vc.index = indexPath.row
142 vc.myProtocol = self!
150 vc.myProtocol = self!
143− vc.channel = ChannelListController.dataSource[indexPath.row]
151+ vc.channel = self?.dataSource[indexPath.row]
144 self?.navigationController?.pushViewController(vc, animated: true)
152 self?.navigationController?.pushViewController(vc, animated: true)
145 }
153 }
146− editButton.backgroundColor = .green
154+ editButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
147
155
148
156
149 let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [weak self] (action, indexPath) in
157 let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [weak self] (action, indexPath) in
158+
159+ Model.channelsDelete(by: self!.dataSource[indexPath.row].id!, completion: {
160+ print("хз что тут делать при успехе")
161+ })
162+
150− if (ChannelListController.dataSource[indexPath.row] == self!.displayingChannel!)
163+ if (self!.dataSource[indexPath.row].id == self!.displayingChannel?.id ?? -1)
151 {
164 {
152− self?.myProtocol?.passData(for: 0, channel: ChannelListController.dataSource[0])
165+ self?.myProtocol?.passData(for: 0, channel: self!.dataSource[0])
153 }
166 }
154 self?.tableView.beginUpdates()
167 self?.tableView.beginUpdates()
155− ChannelListController.dataSource.remove(at: indexPath.row)
168+ self?.dataSource.remove(at: indexPath.row)
156 self?.tableView.deleteRows(at: [indexPath], with: .none)
169 self?.tableView.deleteRows(at: [indexPath], with: .none)
157 self?.tableView.endUpdates()
170 self?.tableView.endUpdates()
158 }
171 }
159− deleteButton.backgroundColor = .red
160
172
173+ deleteButton.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
161
174
162 return [editButton, deleteButton]
175 return [editButton, deleteButton]
163 }
176 }
@@ -170,7 +183,7 @@ class ChannelListController: UITableViewController, DataDelegate {
170 myProtocol?.passData(for: 0, channel: filteredDataSource[indexPath.row])
183 myProtocol?.passData(for: 0, channel: filteredDataSource[indexPath.row])
171 navigationController?.popViewController(animated: true)
184 navigationController?.popViewController(animated: true)
172 } else {
185 } else {
173− myProtocol?.passData(for: 0, channel: ChannelListController.dataSource[indexPath.row])
186+ myProtocol?.passData(for: 0, channel: dataSource[indexPath.row])
174 navigationController?.popViewController(animated: true)
187 navigationController?.popViewController(animated: true)
175 //onChannelSelected?(ChannelListController.dataSource[indexPath.row])
188 //onChannelSelected?(ChannelListController.dataSource[indexPath.row])
176 }
189 }
GDproject/Controller/ News and channels/ChannelsCoordinator.swift+2−2
@@ -12,11 +12,11 @@ import UIKit
12final class ChannelsCoordinator{
12final class ChannelsCoordinator{
13
13
14 // MARK: - Properties
14 // MARK: - Properties
15− private var channel: Channel { didSet { updateInterfaces() } }
15+ private var channel: Model.Channels { didSet { updateInterfaces() } }
16 private weak var navigationController: UINavigationController?
16 private weak var navigationController: UINavigationController?
17
17
18 // MARK:- Init
18 // MARK:- Init
19− init(currentChannel: Channel, navigationController: UINavigationController) {
19+ init(currentChannel: Model.Channels, navigationController: UINavigationController) {
20 self.channel = currentChannel
20 self.channel = currentChannel
21 self.navigationController = navigationController
21 self.navigationController = navigationController
22 }
22 }
GDproject/Controller/ News and channels/FullPostController.swift+19−7
@@ -11,7 +11,8 @@ import TinyConstraints
11
11
12class FullPostController: UITableViewController {
12class FullPostController: UITableViewController {
13
13
14− var post: Post?
14+ var post: Model.Posts?
15+ var type: HeaderType = .NONE
15
16
16 override func viewDidLoad() {
17 override func viewDidLoad() {
17 super.viewDidLoad()
18 super.viewDidLoad()
@@ -20,13 +21,11 @@ class FullPostController: UITableViewController {
20
21
21 setUpNavigationBar()
22 setUpNavigationBar()
22 tableView.separatorStyle = .none
23 tableView.separatorStyle = .none
23−
24−
25 }
24 }
26
25
27 func setUpNavigationBar(){
26 func setUpNavigationBar(){
28− navigationController?.navigationBar.prefersLargeTitles = false
27+ navigationItem.largeTitleDisplayMode = .never
29− navigationItem.title = "\(post?.fromUser.login ?? "")"
28+ navigationItem.title = "\(post?.authorId ?? 0)"
30 navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(self.options))]
29 navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(self.options))]
31 }
30 }
32
31
@@ -83,7 +82,7 @@ class FullPostController: UITableViewController {
83 case 0:
82 case 0:
84 return 1
83 return 1
85 case 1:
84 case 1:
86− return post?.comments.count ?? 0
85+ return 0
87 default:
86 default:
88 return 0
87 return 0
89 }
88 }
@@ -95,7 +94,20 @@ class FullPostController: UITableViewController {
95
94
96 let cell = tableView.dequeueReusableCell(withIdentifier: postCellId) as! PostViewCell
95 let cell = tableView.dequeueReusableCell(withIdentifier: postCellId) as! PostViewCell
97
96
97+
98− cell.fill(with: post!.dataArray, true, post: post!)
98+ cell.fill(with: PostCellData.create(with: post!.body), true, post: post!)
99+
100+ switch type {
101+ case .NEWS:
102+ cell.onUserDisplay = { [weak self] (id) in
103+ let vc = self?.storyboard!.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController
104+ vc.idProfile = id
105+ self?.navigationController?.pushViewController(vc, animated: true)
106+ }
107+ default:
108+ break
109+ }
110+
99 cell.selectionStyle = .none
111 cell.selectionStyle = .none
100 return cell
112 return cell
101 }
113 }
GDproject/Controller/ News and channels/NewPostViewController.swift+87−21
@@ -10,10 +10,15 @@ import UIKit
10import Cartography
10import Cartography
11import Marklight
11import Marklight
12import TinyConstraints
12import TinyConstraints
13+import WSTagsField
13
14
14class NewPostViewController: UIViewController, UITextViewDelegate {
15class NewPostViewController: UIViewController, UITextViewDelegate {
15
16
16 @IBOutlet weak var view1: UIView!
17 @IBOutlet weak var view1: UIView!
18+
19+ @IBOutlet weak var viewForTags: UIView!
20+
21+ fileprivate let tagsField = WSTagsField()
17 // Keep strong instance of the `NSTextStorage` subclass
22 // Keep strong instance of the `NSTextStorage` subclass
18 let textStorage = MarklightTextStorage()
23 let textStorage = MarklightTextStorage()
19
24
@@ -21,6 +26,8 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
21 var myProtocol: NewPostDelegate?
26 var myProtocol: NewPostDelegate?
22
27
23 static var draft: String = ""
28 static var draft: String = ""
29+ static var hashTagsDraft: [String] = []
30+
24 var textView: UITextView!
31 var textView: UITextView!
25
32
26 // buttons for attaching images
33 // buttons for attaching images
@@ -46,9 +53,42 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
46 override func viewDidLoad()
53 override func viewDidLoad()
47 {
54 {
48 super.viewDidLoad()
55 super.viewDidLoad()
56+ //createTimerForUpdates()
57+
49 setUpMD()
58 setUpMD()
50 setUpTextView()
59 setUpTextView()
51 setUpAccessoryView()
60 setUpAccessoryView()
61+ setUpTagsView()
62+ }
63+
64+ func setUpTagsView(){
65+ tagsField.frame = viewForTags.bounds
66+ viewForTags.addSubview(tagsField)
67+
68+ // tagsField.translatesAutoresizingMaskIntoConstraints = false
69+ // tagsField.heightAnchor.constraint(lessThanOrEqualToConstant: 150).isActive = true
70+
71+ tagsField.cornerRadius = 3.0
72+ tagsField.spaceBetweenLines = 10
73+ tagsField.spaceBetweenTags = 10
74+
75+ //tagsField.numberOfLines = 3
76+ //tagsField.maxHeight = 100.0
77+
78+ tagsField.layoutMargins = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
79+ tagsField.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) //old padding
80+
81+ tagsField.placeholder = "Enter a tag"
82+ tagsField.placeholderColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
83+ tagsField.placeholderAlwaysVisible = true
84+ tagsField.backgroundColor = #colorLiteral(red: 0.937254902, green: 0.937254902, blue: 0.9568627451, alpha: 1)
85+ tagsField.returnKeyType = .next
86+ tagsField.delimiter = ""
87+
88+ tagsField.textDelegate = self
89+ tagsField.acceptTagOption = .space
90+
91+ textFieldEvents()
52 }
92 }
53
93
54 override func viewWillAppear(_ animated: Bool) {
94 override func viewWillAppear(_ animated: Bool) {
@@ -57,6 +97,8 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
57 navigationController?.navigationBar.prefersLargeTitles = false
97 navigationController?.navigationBar.prefersLargeTitles = false
58 navigationItem.title = "New post"
98 navigationItem.title = "New post"
59 textView.text = NewPostViewController.draft
99 textView.text = NewPostViewController.draft
100+ tagsField.addTags(NewPostViewController.hashTagsDraft)
101+
60 }
102 }
61
103
62 func setUpAccessoryView(){
104 func setUpAccessoryView(){
@@ -148,23 +190,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
148
190
149 @objc func addMathBrackets()
191 @objc func addMathBrackets()
150 {
192 {
151− if textView.text.count != 0 && textView.text.last != "\n" {
193+ // nothing
152− textView.insertText("\n\\[\n ")
153− }
154− else {
155− textView.insertText("\\[\n ")
156− }
157−
158− if let selectedRange = textView.selectedTextRange
159− {
160− textView.insertText("\n\\]\n")
161−
162− if let newPosition = textView.position(from: selectedRange.start, offset: 0)
163− {
164− // set the new position
165− textView.selectedTextRange = textView.textRange(from: newPosition, to: newPosition)
166− }
167− }
168 }
194 }
169
195
170 func setUpMD(){
196 func setUpMD(){
@@ -178,6 +204,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
178 let layoutManager = NSLayoutManager()
204 let layoutManager = NSLayoutManager()
179
205
180 // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
206 // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
207+ //textStorage.addLayoutManager(textView.layoutManager)
181 textStorage.addLayoutManager(layoutManager)
208 textStorage.addLayoutManager(layoutManager)
182
209
183 let textContainer = NSTextContainer()
210 let textContainer = NSTextContainer()
@@ -186,13 +213,12 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
186 textView = UITextView(frame: view.bounds, textContainer: textContainer)
213 textView = UITextView(frame: view.bounds, textContainer: textContainer)
187 }
214 }
188
215
216+ var indexOfPost = 0
189 // MARK:- new post
217 // MARK:- new post
190 @objc func newPost(){
218 @objc func newPost(){
191− let p = Post(dataArray: [.text(textView.text)], from: User(name: "Сева", id: 5, fullName: "Сева Леонидович"), date: "12.01.19")
219+ Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: tagsField.tags.map { $0.text })
192−
193−
194 // adding row to uiTableView after adding new post
220 // adding row to uiTableView after adding new post
195− myProtocol?.addPost(post: p)
221+ // myProtocol?.addPost(post: p)
196 moveBackToParentVC()
222 moveBackToParentVC()
197 // somewhere here i will be sending server notifications about new post arrival
223 // somewhere here i will be sending server notifications about new post arrival
198 }
224 }
@@ -205,6 +231,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
205 [weak self]
231 [weak self]
206 _ in
232 _ in
207 NewPostViewController.draft = self?.textView.text ?? ""
233 NewPostViewController.draft = self?.textView.text ?? ""
234+ NewPostViewController.hashTagsDraft = self?.tagsField.tags.map { $0.text } ?? []
208 self?.moveBackToParentVC()
235 self?.moveBackToParentVC()
209 }
236 }
210
237
@@ -213,6 +240,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
213 [weak self] (_)
240 [weak self] (_)
214 in
241 in
215 NewPostViewController.draft = ""
242 NewPostViewController.draft = ""
243+ NewPostViewController.hashTagsDraft = []
216 self?.moveBackToParentVC()
244 self?.moveBackToParentVC()
217 }
245 }
218
246
@@ -252,4 +280,42 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
252 rect.size.height = rect.size.height + textView.textContainerInset.bottom
280 rect.size.height = rect.size.height + textView.textContainerInset.bottom
253 textView.scrollRectToVisible(rect, animated: animated)
281 textView.scrollRectToVisible(rect, animated: animated)
254 }
282 }
283+
284+ fileprivate func textFieldEvents() {
285+ tagsField.onDidAddTag = { _, _ in
286+ print("onDidAddTag")
287+ }
288+
289+ tagsField.onDidRemoveTag = { _, _ in
290+ print("onDidRemoveTag")
291+ }
292+
293+ tagsField.onDidChangeText = { _, text in
294+ print("onDidChangeText")
295+ }
296+
297+ tagsField.onDidChangeHeightTo = { _, height in
298+ print("HeightTo \(height)")
299+ }
300+
301+ tagsField.onDidSelectTagView = { _, tagView in
302+ print("Select \(tagView)")
303+ }
304+
305+ tagsField.onDidUnselectTagView = { _, tagView in
306+ print("Unselect \(tagView)")
307+ }
308+ }
309+}
310+
311+
312+extension NewPostViewController: UITextFieldDelegate {
313+
314+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
315+ if textField == tagsField {
316+ textView.becomeFirstResponder()
317+ }
318+ return true
319+ }
320+
255}
321}
GDproject/Controller/ News and channels/NewsController.swift+87−19
@@ -11,24 +11,54 @@ import TinyConstraints
11import Cartography
11import Cartography
12
12
13protocol UpdateableWithChannel: class {
13protocol UpdateableWithChannel: class {
14− var channel: Channel? { get set }
14+ var channel: Model.Channels? { get set }
15}
15}
16protocol NewPostDelegate {
16protocol NewPostDelegate {
17 func addPost(post: Post)
17 func addPost(post: Post)
18}
18}
19−
19+protocol UpdateableWithUser: class {
20+ var user: Model.Users? { get set }
21+}
20// MARK:- Controller with posts and channels availiable.
22// MARK:- Controller with posts and channels availiable.
21// Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels
23// Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels
22class NewsController: UITableViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel, DataDelegate
24class NewsController: UITableViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel, DataDelegate
23{
25{
24− func passData(for row: Int, channel: Channel) {
26+ func passData(for row: Int, channel: Model.Channels) {
27+ if channel.id == -1{
28+ self.channel = nil
29+ } else {
25− self.channel = channel
30+ self.channel = channel
31+ }
26 }
32 }
27
33
28− var channel: Channel?{
34+ var dictionary: [Int: Model.Users]? {
35+ didSet {
36+
37+ var newPosts: [Model.Posts] = []
38+
39+ posts!.forEach({ (post) in
40+
41+ newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: dictionary![post.authorId]!, date: post.updated, tags: post.tags))
42+
43+ post.tags.forEach { Model.Channels.fullTags.insert($0) }
44+
45+ })
46+
47+
48+ news.dataSourse = newPosts
49+ tableView.reloadData()
50+ }
51+ }
52+
53+ var posts: [Model.Posts]? {
29 didSet{
54 didSet{
30− news.dataSourse = channel?.posts ?? []
55+
56+ }
57+ }
58+
59+ var channel: Model.Channels? {
60+ didSet {
31− navigationItem.title = channel?.title ?? ""
61+ navigationItem.title = channel?.name ?? ""
32 }
62 }
33 }
63 }
34
64
@@ -36,37 +66,51 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
36 var onSelectChannel: (() -> Void)?
66 var onSelectChannel: (() -> Void)?
37
67
38 func addPost(post: Post) {
68 func addPost(post: Post) {
39− news.dataSourse.insert(post, at: 0)
69+ //news.dataSourse.insert(post, at: 0)
40 }
70 }
41
71
42 var searchController = UISearchController(searchResultsController: nil)
72 var searchController = UISearchController(searchResultsController: nil)
43
73
44 var news = NewsVC()
74 var news = NewsVC()
45
75
76+ var refreshContr = UIRefreshControl()
77+
46 override func viewDidLoad()
78 override func viewDidLoad()
47 {
79 {
48 super.viewDidLoad()
80 super.viewDidLoad()
49− channel = ChannelListController.dataSource[0]
81+ navigationItem.title = "Loading ..."
50−
82+ tableView.refreshControl = refreshContr
83+ // Configure Refresh Control
84+ refreshContr.addTarget(self, action: #selector(refreshPostsData(_:)), for: .valueChanged)
85+
51 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
86 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
52 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
87 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
53
88
54− searchController.delegate = self
89+ // setUpSearchContr()
55− searchController.obscuresBackgroundDuringPresentation = false
56− navigationItem.searchController = searchController
57− definesPresentationContext = true
58− searchController.searchBar.placeholder = "Search anything"
59
90
60− news.dataSourse = channel!.posts
61 news.viewController = self
91 news.viewController = self
62−
63− news.type = .NONE
92+ news.type = .NEWS
64
93
65 setUpNavigationItemsforPosts()
94 setUpNavigationItemsforPosts()
66
95
67 //setUpBanner()
96 //setUpBanner()
68 }
97 }
69
98
99+ func setUpSearchContr(){
100+ searchController.delegate = self
101+ searchController.obscuresBackgroundDuringPresentation = false
102+ navigationItem.searchController = searchController
103+ definesPresentationContext = true
104+ searchController.searchBar.placeholder = "Search anything"
105+ }
106+
107+ @objc func refreshPostsData( _ ff: UIRefreshControl){
108+ decideWhatChannelDisplay()
109+ }
110+
111+ deinit {
112+ print("news clear")
113+ }
70
114
71 let label : UILabel = {
115 let label : UILabel = {
72 let label = UILabel()
116 let label = UILabel()
@@ -82,7 +126,31 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
82 super.viewWillAppear(animated)
126 super.viewWillAppear(animated)
83 searchController.isActive = false
127 searchController.isActive = false
84 // TODO:- display something if no posts are availiable
128 // TODO:- display something if no posts are availiable
129+
130+ decideWhatChannelDisplay()
131+
85− tableView.reloadData()
132+ // tableView.reloadData()
133+ }
134+
135+ func decideWhatChannelDisplay(){
136+ if let channel = channel, let id = channel.id {
137+
138+ Model.getChannel(with: id) { [weak self] in
139+ self?.posts = $0.posts
140+ self?.dictionary = $0.users
141+ self?.refreshContr.endRefreshing()
142+ }
143+
144+ } else {
145+
146+ Model.getLast { [weak self] in
147+ self?.posts = $0.posts
148+ self?.dictionary = $0.users
149+ self?.refreshContr.endRefreshing()
150+ self?.navigationItem.title = "General"
151+ }
152+
153+ }
86 }
154 }
87
155
88 func setUpNavigationItemsforPosts(){
156 func setUpNavigationItemsforPosts(){
GDproject/Controller/ News and channels/NewsVC.swift+44−4
@@ -7,14 +7,40 @@
7//
7//
8
8
9import UIKit
9import UIKit
10+import MarkdownKit
11+
12+struct PostCellData{
13+ var attributedData = NSAttributedString()
14+
15+ static func create(with attachments: [Model.Attachments]) -> NSAttributedString{
16+ var markdown = ""
17+
18+ attachments.forEach { (attachment) in markdown.append(attachment.markdown) }
19+
20+ let markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 16))
21+ markdownParser.enabledElements = .disabledAutomaticLink
22+ markdownParser.code.textBackgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
23+
24+ return markdownParser.parse(markdown)
25+ }
26+}
10
27
11class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
28class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
12
29
13− var dataSourse: [Post] = []
30+ var dataSourse: [Model.Posts] = [] {
31+ didSet {
32+ cellDataSourse = []
33+ dataSourse.forEach { (item) in
34+ cellDataSourse.append(PostCellData(attributedData: PostCellData.create(with: item.body)))
35+ }
36+ }
37+ }
38+
39+ var cellDataSourse: [PostCellData] = []
14
40
15 var type: HeaderType = .NONE
41 var type: HeaderType = .NONE
16
42
17− var viewController: UIViewController?
43+ weak var viewController: UIViewController?
18
44
19 func numberOfSections(in tableView: UITableView) -> Int {
45 func numberOfSections(in tableView: UITableView) -> Int {
20 return 1
46 return 1
@@ -24,7 +50,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
24 {
50 {
25 tableView.deselectRow(at: indexPath, animated: true)
51 tableView.deselectRow(at: indexPath, animated: true)
26 let vc = viewController!.storyboard!.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController
52 let vc = viewController!.storyboard!.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController
27−
53+ vc.type = type
28 vc.post = dataSourse[indexPath.row]
54 vc.post = dataSourse[indexPath.row]
29 viewController!.navigationController!.pushViewController(vc, animated: true)
55 viewController!.navigationController!.pushViewController(vc, animated: true)
30 }
56 }
@@ -37,7 +63,21 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
37 {
63 {
38 let cell = tableView.dequeueReusableCell(withIdentifier: postCellId, for: indexPath) as! PostViewCell
64 let cell = tableView.dequeueReusableCell(withIdentifier: postCellId, for: indexPath) as! PostViewCell
39
65
40− cell.fill(with: dataSourse[indexPath.row].dataArray, false, post: dataSourse[indexPath.row])
66+ cell.fill(with: cellDataSourse[indexPath.row].attributedData, false, post: dataSourse[indexPath.row])
67+
68+ switch type {
69+ case .NEWS:
70+ cell.onUserDisplay = { [weak self] (id) in
71+ let vc = self?.viewController!.storyboard!.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController
72+ vc.idProfile = id
73+ self?.viewController!.navigationController!.pushViewController(vc, animated: true)
74+ }
75+ case .NONE:
76+ cell.onUserDisplay = { (id) in
77+ print("tapped when profile is open already \(id)")
78+ }
79+ }
80+
41 cell.selectionStyle = .none
81 cell.selectionStyle = .none
42 return cell
82 return cell
43 }
83 }
GDproject/Controller/ News and channels/PostViewCell.swift+51−35
@@ -13,12 +13,14 @@ import TinyConstraints
13
13
14class PostViewCell: UITableViewCell
14class PostViewCell: UITableViewCell
15{
15{
16+ var onUserDisplay: ((Int)->())?
17+
16− let nameLabel: UILabel = {
18+ let nameLabel: UIButton = {
17− let label = UILabel()
19+ let button = UIButton()
18− label.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
20+ button.setTitleColor(.black, for: .normal)
19− label.text = "vbogomazova"
20− label.font = UIFont.boldSystemFont(ofSize: 16)
21+ button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
22+ button.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
21− return label
23+ return button
22 }()
24 }()
23
25
24 let fullNameLabel: UILabel = {
26 let fullNameLabel: UILabel = {
@@ -29,7 +31,7 @@ class PostViewCell: UITableViewCell
29 return label
31 return label
30 }()
32 }()
31
33
32− let commentsLabel: UILabel = {
34+ let timeLabel: UILabel = {
33 let label = UILabel()
35 let label = UILabel()
34 label.font = UIFont.systemFont(ofSize: 12)
36 label.font = UIFont.systemFont(ofSize: 12)
35 label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
37 label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
@@ -51,50 +53,52 @@ class PostViewCell: UITableViewCell
51 fatalError("init(coder:) has not been implemented")
53 fatalError("init(coder:) has not been implemented")
52 }
54 }
53
55
54− func createTextView(with text: String) -> UITextView
56+ func createTextView(with text: NSAttributedString, _ isSelectable: Bool) -> UITextView
55 {
57 {
56− let markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 16))
57− let markdown = text
58− markdownParser.enabledElements = .disabledAutomaticLink
59− markdownParser.header.fontIncrease = 6
60− markdownParser.code.textBackgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
61−
62 let textView = UITextView()
58 let textView = UITextView()
63 textView.isScrollEnabled = false
59 textView.isScrollEnabled = false
64 textView.isEditable = false
60 textView.isEditable = false
61+ if isSelectable {
62+ //textView.isUserInteractionEnabled = false
63+ textView.isSelectable = true
64+ } else {
65− textView.isUserInteractionEnabled = false
65+ textView.isUserInteractionEnabled = false
66+ }
67+
66− textView.attributedText = markdownParser.parse(markdown)
68+ textView.attributedText = text
67 return textView
69 return textView
68 }
70 }
69
71
70 var views: [UIView] = []
72 var views: [UIView] = []
71− var data : [UIImage] = []
73+ var post: Model.Posts?
72− var post: Post?
73
74
74− func fill(with info: [Media], _ isFullVersoin: Bool, post: Post)
75+ func fill(with info: NSAttributedString, _ isFullVersoin: Bool, post: Model.Posts)
75 {
76 {
76 self.post = post
77 self.post = post
77 // important
78 // important
78 contentView.subviews.forEach({ $0.removeFromSuperview() })
79 contentView.subviews.forEach({ $0.removeFromSuperview() })
79
80
80− data = []
81 views = []
81 views = []
82− for item in info.enumerated()
83− {
84− switch item.element
85− {
86− case .text(let text):
87− views.append(createTextView(with: text))
82+ views.append(createTextView(with: info, isFullVersoin))
88− case .image(let images):
83+
89− self.data = images
84+ nameLabel.setTitle("\(post.user?.firstName ?? "") \(post.user?.lastName ?? "")", for: .normal)
90− default:
85+ nameLabel.addTarget(self, action: #selector(displayProfile), for: .touchUpInside)
91− break
86+ if let user = post.user{
87+ fullNameLabel.text = "\(user.firstName) \(user.middleName) \(user.lastName)"
92− }
88+ }
89+ else {
90+ fullNameLabel.text = "\(post.authorId)"
93 }
91 }
92+
94 setUpInStackView(isFullVersoin)
93 setUpInStackView(isFullVersoin)
95 }
94 }
96
95
96+ var hashtags = [String]()
97+
97 func setUpInStackView(_ full : Bool){
98 func setUpInStackView(_ full : Bool){
99+
100+ hashtags = post!.tags
101+
98 let nameStackView = UIStackView(arrangedSubviews: [nameLabel, fullNameLabel])
102 let nameStackView = UIStackView(arrangedSubviews: [nameLabel, fullNameLabel])
99 nameStackView.axis = .vertical
103 nameStackView.axis = .vertical
100 contentView.addSubview(nameStackView)
104 contentView.addSubview(nameStackView)
@@ -113,7 +117,7 @@ class PostViewCell: UITableViewCell
113 scrollView.showsHorizontalScrollIndicator = false
117 scrollView.showsHorizontalScrollIndicator = false
114
118
115 var buttons: [UIButton] = []
119 var buttons: [UIButton] = []
116− for hash in post!.hashtags {
120+ for hash in hashtags {
117 let button = UIButton()
121 let button = UIButton()
118 button.setTitle("#" + hash, for: .normal)
122 button.setTitle("#" + hash, for: .normal)
119 //button.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
123 //button.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
@@ -143,7 +147,13 @@ class PostViewCell: UITableViewCell
143 contentView.addSubview(mainView)
147 contentView.addSubview(mainView)
144 mainView.edgesToSuperview(excluding: [.top, .bottom])
148 mainView.edgesToSuperview(excluding: [.top, .bottom])
145 mainView.topToBottom(of: nameStackView, offset: 5)
149 mainView.topToBottom(of: nameStackView, offset: 5)
150+
151+ // TODO:- change!!!!!!!!!!!!
152+ if hashtags.isEmpty{
153+ mainView.height(0)
154+ } else {
146− mainView.height(30)
155+ mainView.height(30)
156+ }
147
157
148 let stackView = MyStackView(arrangedSubviews: views)
158 let stackView = MyStackView(arrangedSubviews: views)
149 stackView.isFull = full
159 stackView.isFull = full
@@ -157,13 +167,19 @@ class PostViewCell: UITableViewCell
157
167
158 stackView.edgesToSuperview(excluding: [.top, .bottom], insets: .left(16) + .right(16))
168 stackView.edgesToSuperview(excluding: [.top, .bottom], insets: .left(16) + .right(16))
159
169
160− let commentsSharesStackView = UIStackView(arrangedSubviews: [ commentsLabel, shareButton])
170+ let commentsSharesStackView = UIStackView(arrangedSubviews: [ timeLabel, shareButton])
161 contentView.addSubview(commentsSharesStackView)
171 contentView.addSubview(commentsSharesStackView)
162
172
163− commentsLabel.text = "14.02.19 в 23:00"
173+ timeLabel.text = post!.convertDateFormatter()
174+
164 commentsSharesStackView.axis = .horizontal
175 commentsSharesStackView.axis = .horizontal
165 commentsSharesStackView.distribution = .equalSpacing
176 commentsSharesStackView.distribution = .equalSpacing
166 commentsSharesStackView.edgesToSuperview(excluding: .top, insets: .bottom(10) + .left(16) + .right(16))
177 commentsSharesStackView.edgesToSuperview(excluding: .top, insets: .bottom(10) + .left(16) + .right(16))
167 stackView.bottomToTop(of: commentsSharesStackView)
178 stackView.bottomToTop(of: commentsSharesStackView)
168 }
179 }
180+
181+ @objc func displayProfile(){
182+ print("buttonTapped")
183+ onUserDisplay?(post!.authorId)
184+ }
169}
185}
GDproject/Controller/ News and channels/SimplifiedChannelsList.swiftnew file+94
@@ -0,0 +1,94 @@
1+//
2+// SimplifiedChannelsList.swift
3+// GDproject
4+//
5+// Created by cstore on 13/03/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+
10+/// class for ADDing person to existing (or new) channel: like playlist in itunes
11+import UIKit
12+import TinyConstraints
13+
14+class SimplifiedChannelsList: UITableViewController {
15+
16+ var user: Model.Users?
17+
18+ var dataSource: [Model.Channels]?{
19+ didSet{
20+ tableView.reloadData()
21+ }
22+ }
23+
24+ override func viewDidLoad() {
25+ super.viewDidLoad()
26+ setUpNavigationButtons()
27+ navigationItem.title = "All channels"
28+ navigationItem.largeTitleDisplayMode = .never
29+ self.navigationItem.setHidesBackButton(true, animated:true)
30+ tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
31+ }
32+
33+ override func viewWillAppear(_ animated: Bool) {
34+ Model.channelsList { [weak self] (channels) in
35+ self?.dataSource = channels
36+ }
37+ }
38+
39+ func setUpNavigationButtons(){
40+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelAction))
41+ }
42+
43+ func completedActions(with channel: Model.Channels){
44+ Model.updateChannel(with: channel)
45+ cancelAction()
46+ }
47+
48+ @objc func cancelAction(){
49+ let transition = CATransition()
50+ transition.duration = 0.5
51+ transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
52+ transition.type = CATransitionType.reveal
53+ transition.subtype = CATransitionSubtype.fromBottom
54+ navigationController?.view.layer.add(transition, forKey: nil)
55+ navigationController?.popViewController(animated: false)
56+ }
57+
58+ override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
59+ let viewHeader = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 30))
60+ viewHeader.backgroundColor = .white
61+ let label = UILabel()
62+
63+ label.text = "All channels"
64+ label.font = UIFont.boldSystemFont(ofSize: 22)
65+ label.textColor = .black
66+
67+ viewHeader.addSubview(label)
68+ label.edgesToSuperview(insets: .left(16))
69+ return viewHeader
70+ }
71+
72+ override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
73+ return 30
74+ }
75+
76+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
77+ return dataSource?.count ?? 0
78+ }
79+
80+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
81+ var channel = dataSource![indexPath.row]
82+ channel.people.append(user!.id)
83+ completedActions(with: channel)
84+ }
85+
86+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
87+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
88+
89+ cell.textLabel?.text = dataSource![indexPath.row].name
90+ cell.detailTextLabel?.text = "hello"
91+
92+ return cell
93+ }
94+}
GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift+3−2
@@ -2,9 +2,10 @@ import UIKit
2
2
3class TabbarCoordinator{
3class TabbarCoordinator{
4 var window: UIWindow!
4 var window: UIWindow!
5−
5+
6 func start(){
6 func start(){
7− window.rootViewController = UIStoryboard.tabBarController()
7+ let tabbar = UIStoryboard.tabBarController()
8+ window.rootViewController = tabbar
8 }
9 }
9
10
10 init(window: UIWindow) {
11 init(window: UIWindow) {
GDproject/Controller/Log In/LogInCoordinator.swiftnew file+48
@@ -0,0 +1,48 @@
1+//
2+// LogInCoordinator.swift
3+// GDproject
4+//
5+// Created by cstore on 23/02/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class LogInCoordinator{
12+
13+ private weak var navigationController: UINavigationController?
14+ private var window: UIWindow!
15+ // MARK:- Init
16+ init(navigationController: UINavigationController, window: UIWindow) {
17+ self.navigationController = navigationController
18+ self.window = window
19+ }
20+
21+ func start(){
22+ showLogInPage()
23+ }
24+
25+ // MARK: - Private implementation
26+// private func showStatusPage(){
27+// let controller = TabbarCoordinator(window: window)
28+// controller.start()
29+// }
30+
31+ private func showLogInPage(){
32+ let controller = UIStoryboard.makeLogIn()
33+
34+ controller.onLogIn = {
35+ (id) in Model.authenticate(with: id) {
36+ (res) in
37+
38+ if (res) { DataStorage.standard.setUserKey(with: id) }
39+ else { DataStorage.standard.setUserKey(with: 0) }
40+
41+ controller.authenticateSucceeded = res
42+ }
43+ }
44+
45+ navigationController?.pushViewController(controller, animated: false)
46+ }
47+
48+}
GDproject/Controller/LogInViewController.swift → GDproject/Controller/Log In/LogInViewController.swiftrenamed+27−12
@@ -12,6 +12,19 @@ class LogInViewController: UIViewController {
12
12
13 @IBOutlet weak var mailTextField: UITextField!
13 @IBOutlet weak var mailTextField: UITextField!
14
14
15+ @IBOutlet weak var indicatorView: UIActivityIndicatorView!
16+
17+ var onLogIn: ((Int)->())?
18+
19+ var authenticateSucceeded: Bool? {
20+ didSet {
21+ if !authenticateSucceeded! {
22+ indicatorView.stopAnimating()
23+ indicatorView.isHidden = true
24+ }
25+ }
26+ }
27+
15 static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
28 static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
16
29
17 var bottomConstraint: NSLayoutConstraint?
30 var bottomConstraint: NSLayoutConstraint?
@@ -40,14 +53,10 @@ class LogInViewController: UIViewController {
40
53
41 override func viewWillAppear(_ animated: Bool) {
54 override func viewWillAppear(_ animated: Bool) {
42 super.viewWillAppear(animated)
55 super.viewWillAppear(animated)
43− // MARK:- uncomment when mail registration will be availiable
44− //self.navigationController?.setNavigationBarHidden(true, animated: animated)
45 }
56 }
46
57
47 override func viewWillDisappear(_ animated: Bool) {
58 override func viewWillDisappear(_ animated: Bool) {
48 super.viewWillDisappear(animated)
59 super.viewWillDisappear(animated)
49− // MARK:- uncomment when mail registration will be availiable
50− //self.navigationController?.setNavigationBarHidden(false, animated: animated)
51 }
60 }
52
61
53 private func configureTapgesture(){
62 private func configureTapgesture(){
@@ -62,16 +71,20 @@ class LogInViewController: UIViewController {
62 @objc func activateLogInProcess(){
71 @objc func activateLogInProcess(){
63 if logInButton.isEnabled {
72 if logInButton.isEnabled {
64 // MARK:- when log in is succeeded do I need to go there?
73 // MARK:- when log in is succeeded do I need to go there?
65− let vc = storyboard!.instantiateViewController(withIdentifier: newsController) as! NewsController
74+ if let id = Int(mailTextField.text!){
66− present(vc, animated: true) {
75+ indicatorView.isHidden = false
67− print("logged in")
76+ indicatorView.startAnimating()
77+ onLogIn?(id)
78+ view.endEditing(true)
79+ } else {
80+ logInButton.isEnabled = false
81+ logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(0.5), for: .normal)
68 }
82 }
69− DataStorage.standard.setIsLoggedIn(value: true)
70− view.endEditing(true)
71 }
83 }
72 }
84 }
73
85
74 func setUpView(){
86 func setUpView(){
87+ indicatorView.isHidden = true
75 mailTextField.delegate = self
88 mailTextField.delegate = self
76 view.addSubview(keyboardBar)
89 view.addSubview(keyboardBar)
77
90
@@ -80,6 +93,10 @@ class LogInViewController: UIViewController {
80
93
81 setUpBarComponents()
94 setUpBarComponents()
82
95
96+ configureKeyboardBehavior()
97+ }
98+
99+ func configureKeyboardBehavior(){
83 bottomConstraint = NSLayoutConstraint(item: keyboardBar, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0)
100 bottomConstraint = NSLayoutConstraint(item: keyboardBar, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0)
84 view.addConstraint(bottomConstraint!)
101 view.addConstraint(bottomConstraint!)
85
102
@@ -115,9 +132,7 @@ class LogInViewController: UIViewController {
115
132
116 UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
133 UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
117 self.view.layoutIfNeeded()
134 self.view.layoutIfNeeded()
118− }) { (completed) in
135+ })
119−
120− }
121 }
136 }
122 }
137 }
123
138
GDproject/Controller/Profile/BasicInfoController.swift+4−5
@@ -39,15 +39,14 @@ class BasicInfoController: UIViewController, UITableViewDelegate, UITableViewDat
39
39
40 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
40 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
41 if indexPath.row == 0 {
41 if indexPath.row == 0 {
42− let cell = tableView.dequeueReusableCell(withIdentifier: basicInfoCellId, for: indexPath) as! BasicInfoCell
42+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
43− cell.fill(title: dataSourse[indexPath.section].title)
43+ cell.textLabel?.text = dataSourse[indexPath.section].title
44 cell.accessoryType = .disclosureIndicator
44 cell.accessoryType = .disclosureIndicator
45 cell.selectionStyle = .none
45 cell.selectionStyle = .none
46 return cell
46 return cell
47 } else {
47 } else {
48− let cell = tableView.dequeueReusableCell(withIdentifier: infoCellId, for: indexPath) as! InfoCell
48+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
49− cell.fill(title: dataSourse[indexPath.section].sectionData[indexPath.row - 1])
49+ cell.textLabel?.text = dataSourse[indexPath.section].sectionData[indexPath.row - 1]
50− cell.selectionStyle = .none
51 return cell
50 return cell
52 }
51 }
53 }
52 }
GDproject/Controller/Profile/ProfileViewController.swift+81−17
@@ -30,27 +30,53 @@ class ProfileViewController: UIViewController
30
30
31 @IBOutlet weak var newMessageButton: UIButton!
31 @IBOutlet weak var newMessageButton: UIButton!
32
32
33+ var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")]
34+
35+ func fill(with user: Model.Users){
36+ self.facultyLabel.text = "Студент: Факультет Компьютерных Наук"
37+ self.nameLabel.text = "\(user.firstName) \(user.middleName)"
38+ self.surnameLabel.text = "\(user.lastName)"
39+ self.profileImageView.image = protoDictionary[user.id]?.roundedImage
40+ self.placeLabel.text = "📍Москва, Кочновский пр.3"
41+ if user.id == DataStorage.standard.getUserId() {
42+ newMessageButton.isHidden = true
43+ } else {
44+ newMessageButton.isHidden = false
45+ }
46+ }
33
47
48+ var user: Model.Users? {
49+ didSet {
34− func fill(with user: User){
50+ self.fill(with: user!)
35− self.facultyLabel.text = user.faculty ?? ""
51+ Model.getPostsForUser(with: user!.id) { [weak self] (posts) in
36− self.nameLabel.text = "\(user.initials.name) \(user.initials.optional ?? "")"
52+ self?.dataSourse = posts
37− self.surnameLabel.text = user.initials.surname
53+ }
38− self.profileImageView.image = #imageLiteral(resourceName: "kitten").roundedImage
54+ navigationItem.title = "\(user!.firstName) \(user!.lastName)"
39− self.placeLabel.text = user.placeOfWork
55+ }
40 }
56 }
41
57
42− var user: User = User(surname: "Богомазова", name: "Вероника", optional: "Львовна", emailName: "vbogomazova", id: 2, place: "📍Москва, Кочновский пр. 3", faculty: "Методист: Факультет Компьютерных наук")
58+ var basicInfo = BasicInfoController()
59+ var posts = NewsVC()
43
60
44− let basicInfo = BasicInfoController()
61+ var dataSourse: [Model.Posts]?{
45− let posts = NewsVC()
62+ didSet{
63+
64+ var newPosts: [Model.Posts] = []
65+
66+ dataSourse?.forEach({ (post) in
67+ newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: user!, date: post.updated, tags: post.tags))
68+ })
69+
70+ self.posts.dataSourse = newPosts
71+ tableView.reloadData()
72+ }
73+ }
46
74
47 override func viewDidLoad()
75 override func viewDidLoad()
48 {
76 {
49 super.viewDidLoad()
77 super.viewDidLoad()
50
78
51− posts.dataSourse = [Post(dataArray: [.text("Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. comme Aldus PageMaker. est qu'il possède une distribution de lettres plus ou moins normale, et en tout cas comparable avec celle du français standard. De nombreuses suites logicielles de mise en page ou éditeurs de sites Web ont fait du Lorem Ipsum leur faux texte par défaut, et une recherche pour 'Lorem Ipsum' vous conduira vers de nombreux sites qui n'en sont encore qu'à leur phase de construction. Plusieurs versions sont apparues avec le temps, parfois par accident, souvent intentionnellement (histoire d'y rajouter de petits clins d'oeil, voire des phrases embarassantes).")]), Post(dataArray: [.text("Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. Il a été popularisé dans les années 1960 grâce à la vente de feuilles Letraset contenant des passages du Lorem Ipsum, et, plus récemment, par son inclusion dans des applications de mise en page de texte, comme Aldus PageMaker. .")])]
52
79
53− // basicInfo.dataSourse =
54
80
55 posts.viewController = self
81 posts.viewController = self
56 posts.type = .NONE
82 posts.type = .NONE
@@ -65,19 +91,36 @@ class ProfileViewController: UIViewController
65
91
66 posts.viewController = self
92 posts.viewController = self
67
93
68− fill(with: user)
69 tableView.delegate = posts
94 tableView.delegate = posts
70 tableView.dataSource = posts
95 tableView.dataSource = posts
71 tableView.reloadData()
96 tableView.reloadData()
72 }
97 }
73
98
99+ var idProfile: Int?
100+
74 override func viewWillAppear(_ animated: Bool) {
101 override func viewWillAppear(_ animated: Bool) {
102+ if idProfile == nil {
103+ idProfile = DataStorage.standard.getUserId()
104+ }
105+
106+ if let id = idProfile {
107+ if let user = Model.idUser[id] {
108+ self.user = user
109+ } else {
110+ Model.getUsers(for: [id]) { [weak self] (dic) in
111+ self?.user = dic[id]
112+ }
113+ }
114+
115+ // requst for postsforuser was here. moved because of concrr
116+ }
117+
75 setUpNavigarionBar()
118 setUpNavigarionBar()
76 }
119 }
77
120
78 func setUpNavigarionBar(){
121 func setUpNavigarionBar(){
79 navigationController?.navigationBar.prefersLargeTitles = true
122 navigationController?.navigationBar.prefersLargeTitles = true
80− navigationItem.title = user.login
123+ //navigationItem.title = "\(user?.id ?? 0)"
81 let uibarbutton = UIBarButtonItem(title: "More", style: .plain, target: self, action: #selector(showInformation))
124 let uibarbutton = UIBarButtonItem(title: "More", style: .plain, target: self, action: #selector(showInformation))
82 navigationItem.rightBarButtonItems = [uibarbutton]
125 navigationItem.rightBarButtonItems = [uibarbutton]
83 navigationItem.largeTitleDisplayMode = .always
126 navigationItem.largeTitleDisplayMode = .always
@@ -95,7 +138,21 @@ class ProfileViewController: UIViewController
95 // saved
138 // saved
96
139
97 let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
140 let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
98− let editAction = UIAlertAction(title: "Edit profile", style: .default)
141+ let channelAction = UIAlertAction(title: "Add to a channel", style: .default){
142+ [weak self] (_) in
143+
144+ let vc = self?.storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList
145+
146+ vc.user = self?.user!
147+
148+ let transition = CATransition()
149+ transition.duration = 0.5
150+ transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
151+ transition.type = CATransitionType.moveIn
152+ transition.subtype = CATransitionSubtype.fromTop
153+ self?.navigationController?.view.layer.add(transition, forKey: nil)
154+ self?.navigationController?.pushViewController(vc, animated: false)
155+ }
99 let settingsAction = UIAlertAction(title: "Setting", style: .default)
156 let settingsAction = UIAlertAction(title: "Setting", style: .default)
100 { [weak self] (_) in
157 { [weak self] (_) in
101
158
@@ -104,17 +161,24 @@ class ProfileViewController: UIViewController
104 }
161 }
105
162
106 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
163 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
107− let shareAction = UIAlertAction(title: "Share", style: .default)
164+ let logoutAction = UIAlertAction(title: "Log out", style: .destructive)
165+ {
166+ (_) in
167+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
168+ }
108
169
109− optionMenu.addAction(editAction)
170+ optionMenu.addAction(channelAction)
110 optionMenu.addAction(settingsAction)
171 optionMenu.addAction(settingsAction)
111 optionMenu.addAction(copyLink)
172 optionMenu.addAction(copyLink)
112− optionMenu.addAction(shareAction)
173+ optionMenu.addAction(logoutAction)
113 optionMenu.addAction(cancelAction)
174 optionMenu.addAction(cancelAction)
114
175
115 self.present(optionMenu, animated: true, completion: nil)
176 self.present(optionMenu, animated: true, completion: nil)
116 }
177 }
117
178
179+ deinit {
180+ print("profile clear")
181+ }
118
182
119 @IBAction func valueChanged(_ sender: UISegmentedControl) {
183 @IBAction func valueChanged(_ sender: UISegmentedControl) {
120 let index = sender.selectedSegmentIndex
184 let index = sender.selectedSegmentIndex
GDproject/Controller/RepeatingTimer.swiftnew file+59
@@ -0,0 +1,59 @@
1+/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
2+/// crashes that occur from calling resume multiple times on a timer that is
3+/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
4+
5+import Foundation
6+
7+class RepeatingTimer {
8+
9+ let timeInterval: TimeInterval
10+
11+ init(timeInterval: TimeInterval) {
12+ self.timeInterval = timeInterval
13+ }
14+
15+ private lazy var timer: DispatchSourceTimer = {
16+ let t = DispatchSource.makeTimerSource()
17+ t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
18+ t.setEventHandler(handler: { [weak self] in
19+ self?.eventHandler?()
20+ })
21+ return t
22+ }()
23+
24+ var eventHandler: (() -> Void)?
25+
26+ private enum State {
27+ case suspended
28+ case resumed
29+ }
30+
31+ private var state: State = .suspended
32+
33+ deinit {
34+ timer.setEventHandler {}
35+ timer.cancel()
36+ /*
37+ If the timer is suspended, calling cancel without resuming
38+ triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
39+ */
40+ resume()
41+ eventHandler = nil
42+ }
43+
44+ func resume() {
45+ if state == .resumed {
46+ return
47+ }
48+ state = .resumed
49+ timer.resume()
50+ }
51+
52+ func suspend() {
53+ if state == .suspended {
54+ return
55+ }
56+ state = .suspended
57+ timer.suspend()
58+ }
59+}
GDproject/DataStorage.swift+22−7
@@ -7,6 +7,7 @@
7//
7//
8
8
9import Foundation
9import Foundation
10+import UIKit
10
11
11/**
12/**
12 _DataStorage_ class is aimed to store user private settings
13 _DataStorage_ class is aimed to store user private settings
@@ -16,24 +17,37 @@ class DataStorage{
16
17
17 private init(){}
18 private init(){}
18
19
20+ //weak var coordinator: LogInCoordinator?
19 static let standard = DataStorage()
21 static let standard = DataStorage()
20
22
21− // add channel
22−
23− // delete channel
24−
25 /**
23 /**
26 Function for setting log in status of user
24 Function for setting log in status of user
27 */
25 */
28− func setIsLoggedIn(value: Bool){
26+ func setIsLoggedIn(value: Bool, with id: Int){
29 UserDefaults.standard.set(value, forKey: UserDefaultsKeys.loggedIn.rawValue)
27 UserDefaults.standard.set(value, forKey: UserDefaultsKeys.loggedIn.rawValue)
28+ setUserKey(with: id)
29+ isLoggedIn = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue)
30+ }
31+
32+ func setUserKey(with id: Int){
33+ UserDefaults.standard.set(id, forKey: UserDefaultsKeys.id.rawValue)
34+ }
35+
36+ func getUserId() -> Int {
37+ return UserDefaults.standard.integer(forKey: UserDefaultsKeys.id.rawValue)
30 }
38 }
31
39
32 /**
40 /**
33 Function to determine is user logged in already or not
41 Function to determine is user logged in already or not
34 */
42 */
35− func isLoggedIn() -> Bool {
36− return UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue)
43+ var isLoggedIn: Bool = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue) {
44+ didSet{
45+ if isLoggedIn && getUserId() != 0 {
46+ (UIApplication.shared.delegate as? AppDelegate)?.tabCoordinator.start()
47+ } else {
48+ (UIApplication.shared.delegate as? AppDelegate)?.logInAgain()
49+ }
50+ }
37 }
51 }
38}
52}
39
53
@@ -42,4 +56,5 @@ class DataStorage{
42 */
56 */
43enum UserDefaultsKeys: String{
57enum UserDefaultsKeys: String{
44 case loggedIn
58 case loggedIn
59+ case id
45}
60}
GDproject/Extentions.swift+6−2
@@ -52,7 +52,11 @@ extension UIStoryboard {
52 return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "TabBar") as! UITabBarController
52 return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "TabBar") as! UITabBarController
53 }
53 }
54
54
55− class func navChannels() ->UINavigationController{
55+ class func navRoot() -> UINavigationController{
56− return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "navChannels") as! UINavigationController
56+ return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "root") as! UINavigationController
57+ }
58+
59+ class func makeLogIn() -> LogInViewController {
60+ return UIStoryboard.userEdit.instantiateViewController(withIdentifier: logInController) as! LogInViewController
57 }
61 }
58}
62}
GDproject/Simple model/Channel.swift+2−2
@@ -22,9 +22,9 @@ class Channel: Equatable {
22 var hashtags: [String] = []
22 var hashtags: [String] = []
23 var people: [String] = []
23 var people: [String] = []
24
24
25− var posts: [Post] = []
25+ var posts: [Model.Posts] = []
26
26
27− init(title: String, subtitle: String, hashtags: [String], people: [String], posts: [Post])
27+ init(title: String, subtitle: String = "none", hashtags: [String] = [], people: [String] = [], posts: [Model.Posts])
28 {
28 {
29 self.title = title
29 self.title = title
30 self.subtitle = subtitle
30 self.subtitle = subtitle
GDproject/Simple model/Model.swiftnew file+420
@@ -0,0 +1,420 @@
1+//
2+// Model.swift
3+// GDproject
4+//
5+// Created by cstore on 23/02/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import Foundation
10+import Alamofire
11+
12+class Model{
13+
14+ static let invalidTocken = 498
15+
16+ private static var isValidTocken: ((Int)->())? = { responce in
17+ if responce == invalidTocken {
18+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
19+ }
20+ }
21+
22+ private static let baseUrl = "https://valera-denis.herokuapp.com"
23+ static let decoder = JSONDecoder()
24+
25+ static let authenticationURL = URL(string:"\(baseUrl)/authentication/login")!
26+ static let postsLastURL = URL(string:"\(baseUrl)/posts/last")!
27+ static let postsForUserURL = URL(string:"\(baseUrl)/posts/forUser")!
28+ static let postsPublishURL = URL(string:"\(baseUrl)/posts/publish")!
29+ static let usersURL = URL(string:"\(baseUrl)/users")!
30+ static let usersAllURL = URL(string:"\(baseUrl)/users/all")!
31+ static let channelsGetURL = URL(string: "\(baseUrl)/channels/get")!
32+ static let channelsUpdateURL = URL(string: "\(baseUrl)/channels/update")!
33+ static let channelsListURL = URL(string: "\(baseUrl)/channels")!
34+ static let channelsCreateURL = URL(string: "\(baseUrl)/channels/create")!
35+ static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")!
36+
37+
38+ struct QueryPosts: Codable{
39+ var users: [Int: Users]
40+ var posts: [Posts]
41+ }
42+
43+ struct Posts: Codable {
44+ var body: [Attachments]
45+ var authorId: Int
46+ var id: Int
47+ var user: Model.Users?
48+ var updated: String
49+ var tags: [String]
50+
51+ init(body: [Attachments], authorId: Int, id: Int, date: String,tags: [String]) {
52+ self.body = body
53+ self.authorId = authorId
54+ self.id = id
55+ self.updated = date
56+ self.tags = tags
57+ }
58+
59+ init(body: [Attachments], authorId: Int, id: Int, user: Model.Users, date: String, tags: [String]) {
60+ self.body = body
61+ self.authorId = authorId
62+ self.id = id
63+ self.user = user
64+ self.updated = date
65+ self.tags = tags
66+ }
67+
68+ func convertDateFormatter() -> String
69+ {
70+
71+ let dateFormatter = DateFormatter()
72+ dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"//this your string date format
73+ dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
74+ let date = dateFormatter.date(from: updated)
75+
76+ dateFormatter.dateFormat = "MMM d, yyyy HH:mm" ///this is what you want to convert format
77+ dateFormatter.timeZone = NSTimeZone.local
78+ let timeStamp = dateFormatter.string(from: date!)
79+
80+ return timeStamp
81+ }
82+ }
83+
84+ struct CreatedPost: Codable {
85+ var body = [Attachments]()
86+ var tags = [String]()
87+
88+ enum CodingKeys: String, CodingKey {
89+ case body
90+ case tags
91+ }
92+
93+ func encode(to encoder: Encoder) throws {
94+ var container = encoder.container(keyedBy: CodingKeys.self)
95+ try container.encode(body, forKey: .body)
96+ try container.encode(tags, forKey: .tags)
97+ }
98+ }
99+
100+ struct Users: Codable{
101+
102+ var middleName: String
103+ var lastName: String
104+ var firstName: String
105+ var id: Int
106+
107+ }
108+
109+ struct Attachments: Codable {
110+ var markdown: String
111+
112+ init(markdown: String) {
113+ self.markdown = markdown
114+ }
115+
116+ enum CodingKeys: String, CodingKey {
117+ case markdown
118+ }
119+
120+ func encode(to encoder: Encoder) throws {
121+ var container = encoder.container(keyedBy: CodingKeys.self)
122+ try container.encode(markdown, forKey: .markdown)
123+ }
124+ }
125+
126+ struct Channels: Codable {
127+
128+ static var fullTags = Set<String>()
129+ static var fullPeople = [Users]()
130+ static var fullPeopleDict = [Int:Users]()
131+
132+ var people: [Int]
133+ var name: String
134+ var id: Int?
135+ var tags: [String]
136+
137+ init(people: [Int], name: String, id: Int, tags: [String]) {
138+ self.id = id
139+ self.people = people
140+ self.tags = tags
141+ self.name = name
142+ }
143+
144+ init(people: [Int], name: String, tags: [String]) {
145+ self.people = people
146+ self.tags = tags
147+ self.name = name
148+ }
149+
150+ enum CodingKeys: String, CodingKey {
151+ case people
152+ case name
153+ case id
154+ case tags
155+ }
156+
157+ func encode(to encoder: Encoder) throws {
158+ var container = encoder.container(keyedBy: CodingKeys.self)
159+ try container.encode(people, forKey: .people)
160+ try container.encode(name, forKey: .name)
161+ try container.encode(id, forKey: .id)
162+ try container.encode(tags, forKey: .tags)
163+ }
164+ }
165+
166+ static func authenticate(with id: Int, completion: @escaping ((Bool)->())) {
167+
168+ let json: [String:Any] = ["authenticationId" : id]
169+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
170+
171+ var request = URLRequest(url: authenticationURL)
172+ request.httpMethod = "POST"
173+
174+ // insert json data to the request
175+ request.httpBody = jsonData
176+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
177+
178+ AF.request(request).response { (responce) in
179+
180+ guard let realResponse = responce.response, realResponse.statusCode == 200 else
181+ {
182+ print("Not a 200 response")
183+ completion(false)
184+ return
185+ }
186+
187+ guard let json = responce.data else { return }
188+ guard let personId = Int(String(data: json, encoding: String.Encoding.utf8)!) else {
189+ completion(false)
190+ return
191+ }
192+
193+ let fields = realResponse.allHeaderFields as? [String :String]
194+
195+ let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
196+
197+ HTTPCookieStorage.shared.setCookie(cookies[0])
198+
199+ DataStorage.standard.setIsLoggedIn(value: true, with: personId)
200+ completion(true)
201+ }
202+ }
203+
204+
205+ static func getLast(completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){
206+ let jsonString = "30"
207+ var request = URLRequest(url: postsLastURL)
208+
209+ request.httpMethod = "POST"
210+
211+ // insert json data to the request
212+ request.httpBody = jsonString.data(using: .utf8)
213+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
214+
215+ AF.request(request).responseJSON {
216+ (response) in
217+
218+ isValidTocken?(response.response?.statusCode ?? 498)
219+
220+ guard let json = response.data else { return }
221+
222+ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { print("no")
223+ return }
224+
225+ idUser = newQueery.users
226+ completion((newQueery.users, newQueery.posts))
227+ }
228+ }
229+
230+
231+ static func createAndPublish(body: [Attachments], tags: [String]){
232+ let jsonUpd = CreatedPost(body: body, tags: tags)
233+ var request = URLRequest(url: postsPublishURL)
234+
235+ request.httpMethod = "POST"
236+
237+ // insert json data to the request
238+ request.httpBody = try? JSONEncoder().encode(jsonUpd)
239+
240+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
241+ AF.request(request).response {
242+ (response) in
243+
244+ isValidTocken?(response.response?.statusCode ?? 498)
245+ }
246+ }
247+
248+
249+ static func getPostsForUser(with id: Int, completion: @escaping (([Posts])->())){
250+ let json = [
251+ "request" : id,
252+ "limit": 10
253+ ]
254+
255+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
256+
257+ var request = URLRequest(url: postsForUserURL)
258+ request.httpMethod = "POST"
259+
260+ // insert json data to the request
261+ request.httpBody = jsonData
262+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
263+
264+ AF.request(request).responseJSON {
265+ (response) in
266+
267+ isValidTocken?(response.response?.statusCode ?? 498)
268+
269+ guard let json = response.data else { return }
270+
271+ guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return }
272+
273+ completion(newPost.posts)
274+ }
275+ }
276+
277+ static var idUser: [Int:Users] = [:]
278+
279+ static func getUsers(for ids: [Int], completion: @escaping (([Int:Users])->())){
280+ let json = "\(Set(ids))"
281+ print(json)
282+ var request = URLRequest(url: usersURL)
283+
284+ request.httpMethod = "POST"
285+
286+ // insert json data to the request
287+ request.httpBody = json.data(using: .utf8)
288+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
289+
290+ AF.request(request).responseJSON {
291+ (response) in
292+
293+ isValidTocken?(response.response?.statusCode ?? 498)
294+
295+ guard let json = response.data else { return }
296+
297+ guard let users = try? decoder.decode([Users].self, from: json) else { return }
298+
299+ var dict: [Int:Users] = [:]
300+ users.forEach({ (user) in
301+ dict[user.id] = user
302+ })
303+
304+ completion(dict)
305+ }
306+ }
307+
308+ // get channel (with id): in responce -- PostQuery
309+ static func getChannel(with channelId: Int, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){
310+ let json = [
311+ "request" : channelId,
312+ "limit": 10
313+ ]
314+
315+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
316+
317+ var request = URLRequest(url: channelsGetURL)
318+ request.httpMethod = "POST"
319+
320+ // insert json data to the request
321+ request.httpBody = jsonData
322+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
323+
324+ AF.request(request).responseJSON {
325+ (response) in
326+
327+ isValidTocken?(response.response?.statusCode ?? 498)
328+
329+
330+ guard let json = response.data else { return }
331+
332+ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return }
333+
334+ idUser = newQueery.users
335+ completion((newQueery.users, newQueery.posts))
336+ }
337+ }
338+
339+
340+ static func createChannel(with channel: Channels) {
341+
342+ var request = URLRequest(url: channelsCreateURL)
343+ request.httpMethod = "POST"
344+ request.httpBody = try? JSONEncoder().encode(channel)
345+
346+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
347+ AF.request(request).response {
348+ (response) in
349+
350+ isValidTocken?(response.response?.statusCode ?? 498)
351+ }
352+
353+ }
354+
355+ static func channelsList(completion: @escaping (([Channels])->())){
356+ var request = URLRequest(url: channelsListURL)
357+ request.httpMethod = "POST"
358+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
359+
360+ AF.request(request).responseJSON { (response) in
361+
362+ isValidTocken?(response.response?.statusCode ?? 498)
363+
364+ guard let json = response.data else { return }
365+
366+ guard let channelsList = try? decoder.decode([Channels].self, from: json) else { return }
367+
368+ completion(channelsList)
369+ }
370+ }
371+
372+ static func channelsDelete(by id: Int, completion: @escaping(()->())){
373+ var request = URLRequest(url: channelsDeleteURL)
374+ request.httpMethod = "POST"
375+ request.httpBody = "\(id)".data(using: .utf8)
376+ print("\(id)")
377+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
378+
379+ AF.request(request).response { (response) in
380+ isValidTocken?(response.response?.statusCode ?? 498)
381+ }
382+ completion()
383+ }
384+
385+ static func updateChannel(with channel: Channels) {
386+
387+ var request = URLRequest(url: channelsUpdateURL)
388+ request.httpMethod = "POST"
389+ request.httpBody = try? JSONEncoder().encode(channel)
390+
391+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
392+ AF.request(request).response {
393+ (response) in
394+
395+ isValidTocken?(response.response?.statusCode ?? 498)
396+ }
397+ }
398+
399+ static func usersAllGet() {
400+ var request = URLRequest(url: usersAllURL)
401+ request.httpMethod = "POST"
402+
403+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
404+ AF.request(request).response {
405+ (response) in
406+ guard let json = response.data else { return }
407+
408+ guard let users = try? decoder.decode([Users].self, from: json) else { return }
409+
410+ Channels.fullPeople = users
411+ Channels.fullPeopleDict = users.reduce([Int: Users]()) { (dict, person) -> [Int: Users] in
412+ var dict = dict
413+ dict[person.id] = person
414+ return dict
415+ }
416+
417+ isValidTocken?(response.response?.statusCode ?? 498)
418+ }
419+ }
420+}
GDproject/Supporting files/AppDelegate.swift+16−6
@@ -11,7 +11,8 @@ import UIKit
11@UIApplicationMain
11@UIApplicationMain
12class AppDelegate: UIResponder, UIApplicationDelegate {
12class AppDelegate: UIResponder, UIApplicationDelegate {
13
13
14+
14− var coordinator: ChannelsCoordinator!
15+ var coordinator: LogInCoordinator!
15 var tabCoordinator: TabbarCoordinator!
16 var tabCoordinator: TabbarCoordinator!
16
17
17 var window: UIWindow?
18 var window: UIWindow?
@@ -19,16 +20,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
19 return self.window!.rootViewController as? UINavigationController
20 return self.window!.rootViewController as? UINavigationController
20 }
21 }
21
22
22− private func makeCoordinator(with: UINavigationController) -> ChannelsCoordinator {
23+ func logInAgain(){
23− return ChannelsCoordinator(currentChannel: Channel(title: "example", subtitle: "ex", hashtags: ["none"], people: ["none"], posts: []), navigationController: with)
24+ let root = UIStoryboard.navRoot()
25+ window?.rootViewController = root
26+ window?.makeKeyAndVisible()
27+ coordinator = LogInCoordinator(navigationController: root, window: UIApplication.shared.keyWindow!)
28+ coordinator!.start()
24 }
29 }
25
30
31+
26 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
32 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
27 {
33 {
28−
34+ coordinator = LogInCoordinator(navigationController: rootController!, window: window!)
29− // TODO:- determine what to do here: log in or tabbar
30 tabCoordinator = TabbarCoordinator(window: window!)
35 tabCoordinator = TabbarCoordinator(window: window!)
36+ // TODO:- determine what to do here: log in or tabbar
37+ if DataStorage.standard.isLoggedIn {
31− tabCoordinator.start()
38+ tabCoordinator.start()
39+ } else {
40+ coordinator.start()
41+ }
32
42
33 return true
43 return true
34 }
44 }
GDproject/Supporting files/Assets.xcassets/1.imageset/1.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/1.imageset/Contents.jsonnew file+25
@@ -0,0 +1,25 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "1.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ },
21+ "properties" : {
22+ "template-rendering-intent" : "template",
23+ "preserves-vector-representation" : true
24+ }
25+}
GDproject/Supporting files/Assets.xcassets/2.imageset/2.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/2.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "2.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/3.imageset/3.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/3.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "3.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/4.imageset/4.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/4.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "4.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/42.imageset/42.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/42.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "42.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/5.imageset/5.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/5.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "5.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/5051.imageset/5051.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/5051.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "5051.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/69.imageset/69.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/69.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "69.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/9.imageset/9.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/9.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "9.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/share.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "share.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/share.imageset/share.pngnew file

Binary file not shown.