on master help sign in
Cartfile+4−1
since splitsnapshots
unifiedsplit
@@ -1,5 +1,8 @@
1github "Alamofire/Alamofire"
1github "Alamofire/Alamofire"
2−github "WE-St0r/SwiftyVK"
2+# github "WE-St0r/SwiftyVK"
3github "kean/Nuke" ~> 4.0
3github "kean/Nuke" ~> 4.0
4github "realm/realm-cocoa.git"
4github "realm/realm-cocoa.git"
5github "tidwall/SwiftWebSocket"
5github "tidwall/SwiftWebSocket"
6+# github "SwiftyJSON/SwiftyJSON"
7+# github "utahiosmac/Marshal"
8+github "Anviking/Decodable"
Cartfile.resolved+1−1
@@ -1,5 +1,5 @@
1github "Alamofire/Alamofire" "4.5.0"
1github "Alamofire/Alamofire" "4.5.0"
2−github "WE-St0r/SwiftyVK" "2.0.8"
2+github "Anviking/Decodable" "v0.5.1"
3github "kean/Nuke" "4.1.2"
3github "kean/Nuke" "4.1.2"
4github "realm/realm-cocoa" "v2.8.3"
4github "realm/realm-cocoa" "v2.8.3"
5github "tidwall/SwiftWebSocket" "2.6.5"
5github "tidwall/SwiftWebSocket" "2.6.5"
Melk.xcodeproj/project.pbxproj+92−40
@@ -10,8 +10,10 @@
10 0919AEA71F0D3D9D00894FFA /* SecondaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */; };
10 0919AEA71F0D3D9D00894FFA /* SecondaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */; };
11 0919AEA91F0D459600894FFA /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEA81F0D459600894FFA /* CardView.swift */; };
11 0919AEA91F0D459600894FFA /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEA81F0D459600894FFA /* CardView.swift */; };
12 0919AEAD1F0D706700894FFA /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEAC1F0D706700894FFA /* StackView.swift */; };
12 0919AEAD1F0D706700894FFA /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEAC1F0D706700894FFA /* StackView.swift */; };
13+ 09231E351F2394ED007C836D /* CardContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09231E341F2394ED007C836D /* CardContainer.swift */; };
13 09939A561F097DA90014719D /* TableViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09939A551F097DA90014719D /* TableViewCells.swift */; };
14 09939A561F097DA90014719D /* TableViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09939A551F097DA90014719D /* TableViewCells.swift */; };
14 09A456361F0ED9A800BAC32D /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A456351F0ED9A800BAC32D /* CollectionView.swift */; };
15 09A456361F0ED9A800BAC32D /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A456351F0ED9A800BAC32D /* CollectionView.swift */; };
16+ 09AFAB041F159C9200EBD200 /* StreamingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09AFAB031F159C9200EBD200 /* StreamingService.swift */; };
15 09B781591F11630D0007D2BF /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 09B781581F11630D0007D2BF /* .gitignore */; };
17 09B781591F11630D0007D2BF /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 09B781581F11630D0007D2BF /* .gitignore */; };
16 09BF41EF1F07F71F00AFAD57 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */; };
18 09BF41EF1F07F71F00AFAD57 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */; };
17 09BF41F61F07F71F00AFAD57 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 09BF41F51F07F71F00AFAD57 /* Assets.xcassets */; };
19 09BF41F61F07F71F00AFAD57 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 09BF41F51F07F71F00AFAD57 /* Assets.xcassets */; };
@@ -23,37 +25,41 @@
23 09BF42141F08341B00AFAD57 /* PublicCrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42121F08341B00AFAD57 /* PublicCrap.swift */; };
25 09BF42141F08341B00AFAD57 /* PublicCrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42121F08341B00AFAD57 /* PublicCrap.swift */; };
24 09BF42161F0834D400AFAD57 /* VKPublicCrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */; };
26 09BF42161F0834D400AFAD57 /* VKPublicCrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */; };
25 09BF42181F08352E00AFAD57 /* VKData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42171F08352E00AFAD57 /* VKData.swift */; };
27 09BF42181F08352E00AFAD57 /* VKData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42171F08352E00AFAD57 /* VKData.swift */; };
26− 09BF421A1F08357E00AFAD57 /* VkServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42191F08357E00AFAD57 /* VkServices.swift */; };
27− 09BF42261F0835B200AFAD57 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42021F080A5700AFAD57 /* Alamofire.framework */; };
28− 09BF42271F0835B200AFAD57 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42021F080A5700AFAD57 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
29− 09BF42281F0835B200AFAD57 /* Nuke.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42031F080A5700AFAD57 /* Nuke.framework */; };
30− 09BF42291F0835B200AFAD57 /* Nuke.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42031F080A5700AFAD57 /* Nuke.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
31− 09BF422A1F0835B200AFAD57 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42041F080A5700AFAD57 /* Realm.framework */; };
32− 09BF422B1F0835B200AFAD57 /* Realm.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42041F080A5700AFAD57 /* Realm.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
33− 09BF422C1F0835B200AFAD57 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42051F080A5700AFAD57 /* RealmSwift.framework */; };
34− 09BF422D1F0835B200AFAD57 /* RealmSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42051F080A5700AFAD57 /* RealmSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
35− 09BF422E1F0835B200AFAD57 /* SwiftyVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42061F080A5700AFAD57 /* SwiftyVK.framework */; };
36− 09BF422F1F0835B200AFAD57 /* SwiftyVK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42061F080A5700AFAD57 /* SwiftyVK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
37 09BF42321F08388100AFAD57 /* SecondaryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42311F08388100AFAD57 /* SecondaryCoordinator.swift */; };
28 09BF42321F08388100AFAD57 /* SecondaryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42311F08388100AFAD57 /* SecondaryCoordinator.swift */; };
38− 09BF42341F083B5800AFAD57 /* SwiftWebSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */; };
39− 09BF42351F083B6000AFAD57 /* SwiftWebSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */; };
40− 09BF42361F083B6000AFAD57 /* SwiftWebSocket.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
41 09BF42561F08413000AFAD57 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42551F08413000AFAD57 /* MainViewController.swift */; };
29 09BF42561F08413000AFAD57 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BF42551F08413000AFAD57 /* MainViewController.swift */; };
30+ 09F03F231F164E7E000CE916 /* StreamingParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F03F221F164E7E000CE916 /* StreamingParser.swift */; };
31+ 09F03F261F165884000CE916 /* Decodable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09F03F241F165876000CE916 /* Decodable.framework */; };
32+ 09F03F271F165884000CE916 /* Decodable.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09F03F241F165876000CE916 /* Decodable.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
33+ 09F03F281F165884000CE916 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AFAAFF1F159AE300EBD200 /* SwiftyJSON.framework */; };
34+ 09F03F291F165884000CE916 /* SwiftyJSON.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09AFAAFF1F159AE300EBD200 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
35+ 09F03F2A1F165884000CE916 /* SwiftWebSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */; };
36+ 09F03F2B1F165884000CE916 /* SwiftWebSocket.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
37+ 09F03F2C1F165884000CE916 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42021F080A5700AFAD57 /* Alamofire.framework */; };
38+ 09F03F2D1F165884000CE916 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42021F080A5700AFAD57 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
39+ 09F03F2E1F165884000CE916 /* Nuke.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42031F080A5700AFAD57 /* Nuke.framework */; };
40+ 09F03F2F1F165884000CE916 /* Nuke.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42031F080A5700AFAD57 /* Nuke.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
41+ 09F03F301F165884000CE916 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42041F080A5700AFAD57 /* Realm.framework */; };
42+ 09F03F311F165884000CE916 /* Realm.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42041F080A5700AFAD57 /* Realm.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
43+ 09F03F321F165884000CE916 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42051F080A5700AFAD57 /* RealmSwift.framework */; };
44+ 09F03F331F165884000CE916 /* RealmSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 09BF42051F080A5700AFAD57 /* RealmSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
45+ 09F03F361F1674ED000CE916 /* VKServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F03F351F1674ED000CE916 /* VKServer.swift */; };
46+ 09F0D79E1F129DCB00A13751 /* Streaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F0D79D1F129DCB00A13751 /* Streaming.swift */; };
42/* End PBXBuildFile section */
47/* End PBXBuildFile section */
43
48
44/* Begin PBXCopyFilesBuildPhase section */
49/* Begin PBXCopyFilesBuildPhase section */
45− 09BF42301F0835B200AFAD57 /* Embed Frameworks */ = {
50+ 09F03F341F165884000CE916 /* Embed Frameworks */ = {
46 isa = PBXCopyFilesBuildPhase;
51 isa = PBXCopyFilesBuildPhase;
47 buildActionMask = 2147483647;
52 buildActionMask = 2147483647;
48 dstPath = "";
53 dstPath = "";
49 dstSubfolderSpec = 10;
54 dstSubfolderSpec = 10;
50 files = (
55 files = (
51− 09BF422B1F0835B200AFAD57 /* Realm.framework in Embed Frameworks */,
56+ 09F03F311F165884000CE916 /* Realm.framework in Embed Frameworks */,
52− 09BF42271F0835B200AFAD57 /* Alamofire.framework in Embed Frameworks */,
57+ 09F03F291F165884000CE916 /* SwiftyJSON.framework in Embed Frameworks */,
53− 09BF42361F083B6000AFAD57 /* SwiftWebSocket.framework in Embed Frameworks */,
54− 09BF422F1F0835B200AFAD57 /* SwiftyVK.framework in Embed Frameworks */,
55− 09BF422D1F0835B200AFAD57 /* RealmSwift.framework in Embed Frameworks */,
58+ 09F03F331F165884000CE916 /* RealmSwift.framework in Embed Frameworks */,
59+ 09F03F271F165884000CE916 /* Decodable.framework in Embed Frameworks */,
56− 09BF42291F0835B200AFAD57 /* Nuke.framework in Embed Frameworks */,
60+ 09F03F2F1F165884000CE916 /* Nuke.framework in Embed Frameworks */,
61+ 09F03F2B1F165884000CE916 /* SwiftWebSocket.framework in Embed Frameworks */,
62+ 09F03F2D1F165884000CE916 /* Alamofire.framework in Embed Frameworks */,
57 );
63 );
58 name = "Embed Frameworks";
64 name = "Embed Frameworks";
59 runOnlyForDeploymentPostprocessing = 0;
65 runOnlyForDeploymentPostprocessing = 0;
@@ -64,8 +70,11 @@
64 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryViewController.swift; sourceTree = "<group>"; };
70 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryViewController.swift; sourceTree = "<group>"; };
65 0919AEA81F0D459600894FFA /* CardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = "<group>"; };
71 0919AEA81F0D459600894FFA /* CardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = "<group>"; };
66 0919AEAC1F0D706700894FFA /* StackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; };
72 0919AEAC1F0D706700894FFA /* StackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; };
73+ 09231E341F2394ED007C836D /* CardContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardContainer.swift; sourceTree = "<group>"; };
67 09939A551F097DA90014719D /* TableViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCells.swift; sourceTree = "<group>"; };
74 09939A551F097DA90014719D /* TableViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCells.swift; sourceTree = "<group>"; };
68 09A456351F0ED9A800BAC32D /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = "<group>"; };
75 09A456351F0ED9A800BAC32D /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = "<group>"; };
76+ 09AFAAFF1F159AE300EBD200 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/iOS/SwiftyJSON.framework; sourceTree = SOURCE_ROOT; };
77+ 09AFAB031F159C9200EBD200 /* StreamingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamingService.swift; sourceTree = "<group>"; };
69 09B781581F11630D0007D2BF /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = .gitignore; path = ../.gitignore; sourceTree = "<group>"; };
78 09B781581F11630D0007D2BF /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = .gitignore; path = ../.gitignore; sourceTree = "<group>"; };
70 09BF41EB1F07F71F00AFAD57 /* Melk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Melk.app; sourceTree = BUILT_PRODUCTS_DIR; };
79 09BF41EB1F07F71F00AFAD57 /* Melk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Melk.app; sourceTree = BUILT_PRODUCTS_DIR; };
71 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
80 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -77,17 +86,19 @@
77 09BF42031F080A5700AFAD57 /* Nuke.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nuke.framework; path = Carthage/Build/iOS/Nuke.framework; sourceTree = SOURCE_ROOT; };
86 09BF42031F080A5700AFAD57 /* Nuke.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nuke.framework; path = Carthage/Build/iOS/Nuke.framework; sourceTree = SOURCE_ROOT; };
78 09BF42041F080A5700AFAD57 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = SOURCE_ROOT; };
87 09BF42041F080A5700AFAD57 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = SOURCE_ROOT; };
79 09BF42051F080A5700AFAD57 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = SOURCE_ROOT; };
88 09BF42051F080A5700AFAD57 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = SOURCE_ROOT; };
80− 09BF42061F080A5700AFAD57 /* SwiftyVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyVK.framework; path = Carthage/Build/iOS/SwiftyVK.framework; sourceTree = SOURCE_ROOT; };
81 09BF420D1F0833A700AFAD57 /* LocalVKDataTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalVKDataTypes.swift; sourceTree = "<group>"; };
89 09BF420D1F0833A700AFAD57 /* LocalVKDataTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalVKDataTypes.swift; sourceTree = "<group>"; };
82 09BF420F1F0833C100AFAD57 /* MainCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = "<group>"; };
90 09BF420F1F0833C100AFAD57 /* MainCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = "<group>"; };
83 09BF42111F08341B00AFAD57 /* MyWeirdness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyWeirdness.swift; sourceTree = "<group>"; };
91 09BF42111F08341B00AFAD57 /* MyWeirdness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyWeirdness.swift; sourceTree = "<group>"; };
84 09BF42121F08341B00AFAD57 /* PublicCrap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicCrap.swift; sourceTree = "<group>"; };
92 09BF42121F08341B00AFAD57 /* PublicCrap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublicCrap.swift; sourceTree = "<group>"; };
85 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VKPublicCrap.swift; sourceTree = "<group>"; };
93 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VKPublicCrap.swift; sourceTree = "<group>"; };
86 09BF42171F08352E00AFAD57 /* VKData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VKData.swift; sourceTree = "<group>"; };
94 09BF42171F08352E00AFAD57 /* VKData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VKData.swift; sourceTree = "<group>"; };
87− 09BF42191F08357E00AFAD57 /* VkServices.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VkServices.swift; sourceTree = "<group>"; };
88 09BF42311F08388100AFAD57 /* SecondaryCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryCoordinator.swift; sourceTree = "<group>"; };
95 09BF42311F08388100AFAD57 /* SecondaryCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryCoordinator.swift; sourceTree = "<group>"; };
89 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftWebSocket.framework; path = Carthage/Build/iOS/SwiftWebSocket.framework; sourceTree = SOURCE_ROOT; };
96 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftWebSocket.framework; path = Carthage/Build/iOS/SwiftWebSocket.framework; sourceTree = SOURCE_ROOT; };
90 09BF42551F08413000AFAD57 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
97 09BF42551F08413000AFAD57 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
98+ 09F03F221F164E7E000CE916 /* StreamingParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamingParser.swift; sourceTree = "<group>"; };
99+ 09F03F241F165876000CE916 /* Decodable.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Decodable.framework; path = Carthage/Build/iOS/Decodable.framework; sourceTree = SOURCE_ROOT; };
100+ 09F03F351F1674ED000CE916 /* VKServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VKServer.swift; sourceTree = "<group>"; };
101+ 09F0D79D1F129DCB00A13751 /* Streaming.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Streaming.swift; sourceTree = "<group>"; };
91/* End PBXFileReference section */
102/* End PBXFileReference section */
92
103
93/* Begin PBXFrameworksBuildPhase section */
104/* Begin PBXFrameworksBuildPhase section */
@@ -95,19 +106,57 @@
95 isa = PBXFrameworksBuildPhase;
106 isa = PBXFrameworksBuildPhase;
96 buildActionMask = 2147483647;
107 buildActionMask = 2147483647;
97 files = (
108 files = (
98− 09BF422A1F0835B200AFAD57 /* Realm.framework in Frameworks */,
109+ 09F03F301F165884000CE916 /* Realm.framework in Frameworks */,
99− 09BF42261F0835B200AFAD57 /* Alamofire.framework in Frameworks */,
110+ 09F03F281F165884000CE916 /* SwiftyJSON.framework in Frameworks */,
100− 09BF42341F083B5800AFAD57 /* SwiftWebSocket.framework in Frameworks */,
101− 09BF422E1F0835B200AFAD57 /* SwiftyVK.framework in Frameworks */,
102− 09BF422C1F0835B200AFAD57 /* RealmSwift.framework in Frameworks */,
111+ 09F03F321F165884000CE916 /* RealmSwift.framework in Frameworks */,
112+ 09F03F261F165884000CE916 /* Decodable.framework in Frameworks */,
103− 09BF42281F0835B200AFAD57 /* Nuke.framework in Frameworks */,
113+ 09F03F2E1F165884000CE916 /* Nuke.framework in Frameworks */,
104− 09BF42351F083B6000AFAD57 /* SwiftWebSocket.framework in Frameworks */,
114+ 09F03F2A1F165884000CE916 /* SwiftWebSocket.framework in Frameworks */,
115+ 09F03F2C1F165884000CE916 /* Alamofire.framework in Frameworks */,
105 );
116 );
106 runOnlyForDeploymentPostprocessing = 0;
117 runOnlyForDeploymentPostprocessing = 0;
107 };
118 };
108/* End PBXFrameworksBuildPhase section */
119/* End PBXFrameworksBuildPhase section */
109
120
110/* Begin PBXGroup section */
121/* Begin PBXGroup section */
122+ 09231E331F2394C5007C836D /* Card */ = {
123+ isa = PBXGroup;
124+ children = (
125+ 0919AEA81F0D459600894FFA /* CardView.swift */,
126+ 09231E341F2394ED007C836D /* CardContainer.swift */,
127+ );
128+ name = Card;
129+ sourceTree = "<group>";
130+ };
131+ 098397F71F1E887F008C88DD /* Main */ = {
132+ isa = PBXGroup;
133+ children = (
134+ 09BF420F1F0833C100AFAD57 /* MainCoordinator.swift */,
135+ 09BF42551F08413000AFAD57 /* MainViewController.swift */,
136+ 09939A551F097DA90014719D /* TableViewCells.swift */,
137+ );
138+ name = Main;
139+ sourceTree = "<group>";
140+ };
141+ 098397F81F1E888F008C88DD /* Secondary */ = {
142+ isa = PBXGroup;
143+ children = (
144+ 09BF42311F08388100AFAD57 /* SecondaryCoordinator.swift */,
145+ 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */,
146+ );
147+ name = Secondary;
148+ sourceTree = "<group>";
149+ };
150+ 098397F91F1E88A3008C88DD /* Streaming */ = {
151+ isa = PBXGroup;
152+ children = (
153+ 09AFAB031F159C9200EBD200 /* StreamingService.swift */,
154+ 09F03F221F164E7E000CE916 /* StreamingParser.swift */,
155+ 09F0D79D1F129DCB00A13751 /* Streaming.swift */,
156+ );
157+ name = Streaming;
158+ sourceTree = "<group>";
159+ };
111 09A456341F0ED98800BAC32D /* Public */ = {
160 09A456341F0ED98800BAC32D /* Public */ = {
112 isa = PBXGroup;
161 isa = PBXGroup;
113 children = (
162 children = (
@@ -136,18 +185,16 @@
136 09BF41ED1F07F71F00AFAD57 /* Melk */ = {
185 09BF41ED1F07F71F00AFAD57 /* Melk */ = {
137 isa = PBXGroup;
186 isa = PBXGroup;
138 children = (
187 children = (
188+ 098397F71F1E887F008C88DD /* Main */,
189+ 098397F81F1E888F008C88DD /* Secondary */,
190+ 098397F91F1E88A3008C88DD /* Streaming */,
191+ 09231E331F2394C5007C836D /* Card */,
139 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */,
192 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */,
140 09BF42111F08341B00AFAD57 /* MyWeirdness.swift */,
193 09BF42111F08341B00AFAD57 /* MyWeirdness.swift */,
141 09BF42121F08341B00AFAD57 /* PublicCrap.swift */,
194 09BF42121F08341B00AFAD57 /* PublicCrap.swift */,
142− 09BF420F1F0833C100AFAD57 /* MainCoordinator.swift */,
195+ 09F03F351F1674ED000CE916 /* VKServer.swift */,
143− 09BF42311F08388100AFAD57 /* SecondaryCoordinator.swift */,
144− 0919AEA81F0D459600894FFA /* CardView.swift */,
145− 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */,
146− 09BF42551F08413000AFAD57 /* MainViewController.swift */,
147− 09939A551F097DA90014719D /* TableViewCells.swift */,
148 09BF420D1F0833A700AFAD57 /* LocalVKDataTypes.swift */,
196 09BF420D1F0833A700AFAD57 /* LocalVKDataTypes.swift */,
149 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */,
197 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */,
150− 09BF42191F08357E00AFAD57 /* VkServices.swift */,
151 09BF42171F08352E00AFAD57 /* VKData.swift */,
198 09BF42171F08352E00AFAD57 /* VKData.swift */,
152 09BF41F51F07F71F00AFAD57 /* Assets.xcassets */,
199 09BF41F51F07F71F00AFAD57 /* Assets.xcassets */,
153 09BF41F71F07F71F00AFAD57 /* LaunchScreen.storyboard */,
200 09BF41F71F07F71F00AFAD57 /* LaunchScreen.storyboard */,
@@ -163,12 +210,13 @@
163 09BF420C1F080A5F00AFAD57 /* Dependencies */ = {
210 09BF420C1F080A5F00AFAD57 /* Dependencies */ = {
164 isa = PBXGroup;
211 isa = PBXGroup;
165 children = (
212 children = (
213+ 09F03F241F165876000CE916 /* Decodable.framework */,
214+ 09AFAAFF1F159AE300EBD200 /* SwiftyJSON.framework */,
166 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */,
215 09BF42331F083B5800AFAD57 /* SwiftWebSocket.framework */,
167 09BF42021F080A5700AFAD57 /* Alamofire.framework */,
216 09BF42021F080A5700AFAD57 /* Alamofire.framework */,
168 09BF42031F080A5700AFAD57 /* Nuke.framework */,
217 09BF42031F080A5700AFAD57 /* Nuke.framework */,
169 09BF42041F080A5700AFAD57 /* Realm.framework */,
218 09BF42041F080A5700AFAD57 /* Realm.framework */,
170 09BF42051F080A5700AFAD57 /* RealmSwift.framework */,
219 09BF42051F080A5700AFAD57 /* RealmSwift.framework */,
171− 09BF42061F080A5700AFAD57 /* SwiftyVK.framework */,
172 );
220 );
173 name = Dependencies;
221 name = Dependencies;
174 sourceTree = "<group>";
222 sourceTree = "<group>";
@@ -183,7 +231,7 @@
183 09BF41E71F07F71F00AFAD57 /* Sources */,
231 09BF41E71F07F71F00AFAD57 /* Sources */,
184 09BF41E81F07F71F00AFAD57 /* Frameworks */,
232 09BF41E81F07F71F00AFAD57 /* Frameworks */,
185 09BF41E91F07F71F00AFAD57 /* Resources */,
233 09BF41E91F07F71F00AFAD57 /* Resources */,
186− 09BF42301F0835B200AFAD57 /* Embed Frameworks */,
234+ 09F03F341F165884000CE916 /* Embed Frameworks */,
187 );
235 );
188 buildRules = (
236 buildRules = (
189 );
237 );
@@ -249,17 +297,21 @@
249 buildActionMask = 2147483647;
297 buildActionMask = 2147483647;
250 files = (
298 files = (
251 09939A561F097DA90014719D /* TableViewCells.swift in Sources */,
299 09939A561F097DA90014719D /* TableViewCells.swift in Sources */,
300+ 09F03F361F1674ED000CE916 /* VKServer.swift in Sources */,
252 0919AEAD1F0D706700894FFA /* StackView.swift in Sources */,
301 0919AEAD1F0D706700894FFA /* StackView.swift in Sources */,
253 09BF42141F08341B00AFAD57 /* PublicCrap.swift in Sources */,
302 09BF42141F08341B00AFAD57 /* PublicCrap.swift in Sources */,
254 09BF42161F0834D400AFAD57 /* VKPublicCrap.swift in Sources */,
303 09BF42161F0834D400AFAD57 /* VKPublicCrap.swift in Sources */,
304+ 09F0D79E1F129DCB00A13751 /* Streaming.swift in Sources */,
255 09BF42181F08352E00AFAD57 /* VKData.swift in Sources */,
305 09BF42181F08352E00AFAD57 /* VKData.swift in Sources */,
256 09BF42101F0833C100AFAD57 /* MainCoordinator.swift in Sources */,
306 09BF42101F0833C100AFAD57 /* MainCoordinator.swift in Sources */,
257 09A456361F0ED9A800BAC32D /* CollectionView.swift in Sources */,
307 09A456361F0ED9A800BAC32D /* CollectionView.swift in Sources */,
258− 09BF421A1F08357E00AFAD57 /* VkServices.swift in Sources */,
308+ 09231E351F2394ED007C836D /* CardContainer.swift in Sources */,
259 09BF42131F08341B00AFAD57 /* MyWeirdness.swift in Sources */,
309 09BF42131F08341B00AFAD57 /* MyWeirdness.swift in Sources */,
260 0919AEA91F0D459600894FFA /* CardView.swift in Sources */,
310 0919AEA91F0D459600894FFA /* CardView.swift in Sources */,
261 09BF42321F08388100AFAD57 /* SecondaryCoordinator.swift in Sources */,
311 09BF42321F08388100AFAD57 /* SecondaryCoordinator.swift in Sources */,
262 09BF41EF1F07F71F00AFAD57 /* AppDelegate.swift in Sources */,
312 09BF41EF1F07F71F00AFAD57 /* AppDelegate.swift in Sources */,
313+ 09AFAB041F159C9200EBD200 /* StreamingService.swift in Sources */,
314+ 09F03F231F164E7E000CE916 /* StreamingParser.swift in Sources */,
263 09BF420E1F0833A700AFAD57 /* LocalVKDataTypes.swift in Sources */,
315 09BF420E1F0833A700AFAD57 /* LocalVKDataTypes.swift in Sources */,
264 09BF42561F08413000AFAD57 /* MainViewController.swift in Sources */,
316 09BF42561F08413000AFAD57 /* MainViewController.swift in Sources */,
265 0919AEA71F0D3D9D00894FFA /* SecondaryViewController.swift in Sources */,
317 0919AEA71F0D3D9D00894FFA /* SecondaryViewController.swift in Sources */,
Melk.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/Melk.xcscheme+3−3
@@ -42,9 +42,9 @@
42 </AdditionalOptions>
42 </AdditionalOptions>
43 </TestAction>
43 </TestAction>
44 <LaunchAction
44 <LaunchAction
45− buildConfiguration = "Debug"
45+ buildConfiguration = "Release"
46− selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
46+ selectedDebuggerIdentifier = ""
47− selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
47+ selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
48 launchStyle = "0"
48 launchStyle = "0"
49 useCustomWorkingDirectory = "NO"
49 useCustomWorkingDirectory = "NO"
50 ignoresPersistentStateOnLaunch = "NO"
50 ignoresPersistentStateOnLaunch = "NO"
Melk/AppDelegate.swift+7
@@ -7,6 +7,7 @@
7//
7//
8
8
9import UIKit
9import UIKit
10+//import SwiftyVK
10
11
11@UIApplicationMain
12@UIApplicationMain
12class AppDelegate: UIResponder, UIApplicationDelegate {
13class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -22,6 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
22 mainWindow.screen = UIScreen.main
23 mainWindow.screen = UIScreen.main
23 mainWindow.makeKeyAndVisible()
24 mainWindow.makeKeyAndVisible()
24 coordinator = MainCoordinator(window: mainWindow)
25 coordinator = MainCoordinator(window: mainWindow)
26+ coordinator.start()
25
27
26 NotificationCenter.default.addObserver(forName: NSNotification.Name.UIScreenDidConnect, object: nil, queue: OperationQueue.main) { (_) in
28 NotificationCenter.default.addObserver(forName: NSNotification.Name.UIScreenDidConnect, object: nil, queue: OperationQueue.main) { (_) in
27 self.updateScreens()
29 self.updateScreens()
@@ -34,6 +36,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
34 // Override point for customization after application launch.
36 // Override point for customization after application launch.
35 return true
37 return true
36 }
38 }
39+
40+ func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
41+ return true
42+ }
43+
37
44
38 func applicationWillResignActive(_ application: UIApplication) {
45 func applicationWillResignActive(_ application: UIApplication) {
39 // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
46 // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Melk/CardContainer.swiftnew file+100
@@ -0,0 +1,100 @@
1+//
2+// CardContainer.swift
3+// Melk
4+//
5+// Created by Ilya Kos on 7/22/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class CardContainer: UIView {
12+
13+ func animate() {
14+ blurView.effect = blur
15+ card.transform = smallTransform
16+ card.layer.opacity = 0
17+ shadowView.layer.opacity = 0
18+ shadowView.transform = smallTransform
19+ shadowView.layer.shadowRadius = 0
20+ UIView.animate(withDuration: 0.5, delay: 0, options: [.curveEaseInOut], animations: {
21+ self.card.transform = self.normalTransform
22+ self.shadowView.transform = self.normalTransform
23+ self.shadowView.layer.shadowRadius = self.shadowRadius
24+ self.shadowView.layer.opacity = 1
25+ self.blurView.effect = nil
26+ self.card.layer.opacity = 1
27+ }, completion: nil)
28+ }
29+
30+ // MARK: Passthrough to CardView
31+ var post: WallPost? {
32+ get {
33+ return card.post
34+ }
35+ set {
36+ card.post = newValue
37+ }
38+ }
39+
40+ var minWidth: CGFloat {
41+ get {
42+ return card.minWidth
43+ }
44+ set {
45+ card.minWidth = newValue
46+ }
47+ }
48+
49+
50+ var height: CGFloat {
51+ get {
52+ return card.height
53+ }
54+ set {
55+ card.height = newValue
56+ }
57+ }
58+
59+ private let card: CardView
60+ private let blurView = UIVisualEffectView()
61+ private let shadowView = UIView()
62+ private let blur = UIBlurEffect(style: UIBlurEffectStyle.light)
63+ private let scale: CGFloat = 0.5
64+ private let smallTransform: CGAffineTransform
65+ private let normalTransform = CGAffineTransform(scaleX: 1, y: 1)
66+ private let shadowRadius: CGFloat = 12
67+ private let shadowOpacity: Float = 0.15
68+ private let shadowOffset: CGFloat = 16
69+
70+ override init(frame: CGRect) {
71+ card = CardView(frame: frame)
72+ smallTransform = CGAffineTransform(scaleX: scale, y: scale)
73+ super.init(frame: frame)
74+ shadowView.layer.shadowColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
75+ shadowView.layer.shadowRadius = shadowRadius
76+ shadowView.layer.shadowOpacity = shadowOpacity
77+ shadowView.backgroundColor = UIColor.clear
78+ shadowView.clipsToBounds = false
79+ shadowView.layer.shadowOffset = CGSize.zero
80+ addSubview(shadowView)
81+ addSubview(card)
82+ addSubview(blurView)
83+ }
84+
85+ required init?(coder aDecoder: NSCoder) {
86+ fatalError("init(coder:) has not been implemented")
87+ }
88+
89+ override func layoutSubviews() {
90+ super.layoutSubviews()
91+ card.layoutIfNeeded()
92+ self.frame = card.frame
93+ shadowView.layer.shadowPath = CGPath(roundedRect: card.frame, cornerWidth: card.layer.cornerRadius, cornerHeight: card.layer.cornerRadius,
94+ transform: nil)
95+ shadowView.layer.shadowOffset.height = shadowOffset
96+ shadowView.frame = card.frame
97+ blurView.frame = CGRect(x: -shadowRadius, y: 0, width: card.frame.width + shadowRadius*2, height: card.frame.height + shadowRadius + shadowOffset)
98+ }
99+
100+}
Melk/CardView.swift+126−10
@@ -12,20 +12,51 @@ class CardView: UIView {
12
12
13 var post: WallPost? {
13 var post: WallPost? {
14 didSet {
14 didSet {
15+// print(post?.owner)
15 update()
16 update()
16 }
17 }
17 }
18 }
19+ var minWidth: CGFloat = 300 {
20+ didSet {
21+// update()
22+ }
23+ }
24+ var height: CGFloat = 400 {
25+ didSet {
26+// update()
27+ }
28+ }
18
29
19 override init(frame: CGRect) {
30 override init(frame: CGRect) {
20 super.init(frame: frame)
31 super.init(frame: frame)
21 layer.masksToBounds = true
32 layer.masksToBounds = true
22 layer.cornerRadius = 20
33 layer.cornerRadius = 20
34+ gradient.colors = [#colorLiteral(red: 0.9841352105, green: 0.9841352105, blue: 0.9841352105, alpha: 1).cgColor, #colorLiteral(red: 0.9456828237, green: 0.9456828237, blue: 0.9456828237, alpha: 1).cgColor]
35+ gradient.startPoint = CGPoint(x: 0.5, y: 0)
36+ gradient.endPoint = CGPoint(x: 0.5, y: 1)
37+ layer.addSublayer(gradient)
23− backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
38+// backgroundColor = #colorLiteral(red: 0.9638966278, green: 0.9734401588, blue: 0.9734401588, alpha: 1)
24 }
39 }
25
40
26 required init?(coder aDecoder: NSCoder) {
41 required init?(coder aDecoder: NSCoder) {
27 fatalError("init(coder:) has not been implemented")
42 fatalError("init(coder:) has not been implemented")
28 }
43 }
44+
45+ private let gradient = CAGradientLayer()
46+
47+
48+ private let padding = CGPoint(x: 32, y: 32)
49+ private let vPadding1: CGFloat = 16
50+ private let vPadding2: CGFloat = 16
51+ private var minTextHeight: CGFloat {
52+ return CGFloat(height * 0.2)
53+ }
54+ private let gradientRatio: CGFloat = 0.2
55+ private let minGradientAmount: CGFloat = 70
56+ private let hiddenByGradient: CGFloat = 10
57+ private var maxAttachmentDim: CGFloat {
58+ return minWidth / 2.3
59+ }
29
60
30// private let avatar = UIImage()
61// private let avatar = UIImage()
31// private let name = UIImage()
62// private let name = UIImage()
@@ -34,14 +65,13 @@ class CardView: UIView {
34 subviews.map {$0.removeFromSuperview()}
65 subviews.map {$0.removeFromSuperview()}
35
66
36 let largeStack = StackView(axis: .vertical, alignment: .left)
67 let largeStack = StackView(axis: .vertical, alignment: .left)
37− largeStack.padding = CGPoint(x: 32, y: 32)
68+ largeStack.padding = padding
38 addSubview(largeStack)
69 addSubview(largeStack)
39
70
40 let userView = StackView(axis: .horizontal, alignment: .center)
71 let userView = StackView(axis: .horizontal, alignment: .center)
41−// userView.backgroundColor = #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1)
42
72
43
73
44− let avatarDim: CGFloat = 50
74+ let avatarDim: CGFloat = 64
45 let avatar = UIImageView()
75 let avatar = UIImageView()
46 post?.owner?.avatar?.load(into: avatar)
76 post?.owner?.avatar?.load(into: avatar)
47 avatar.layer.cornerRadius = avatarDim/2
77 avatar.layer.cornerRadius = avatarDim/2
@@ -51,30 +81,116 @@ class CardView: UIView {
51 avatar.frame.size = CGSize(width: avatarDim, height: avatarDim)
81 avatar.frame.size = CGSize(width: avatarDim, height: avatarDim)
52 userView.addArrangedSubview(.view(avatar))
82 userView.addArrangedSubview(.view(avatar))
53
83
54− userView.addArrangedSubview(.spacer(8))
84+ userView.addArrangedSubview(.spacer(16))
55
85
56 let userName = UILabel()
86 let userName = UILabel()
57 let firstName = post?.owner?.firstName ?? ""
87 let firstName = post?.owner?.firstName ?? ""
58 let lastName = post?.owner?.lastName ?? ""
88 let lastName = post?.owner?.lastName ?? ""
59 userName.text = "\(firstName) \(lastName)"
89 userName.text = "\(firstName) \(lastName)"
60 userName.font = UIFont.systemFont(ofSize: 28, weight: UIFontWeightHeavy)
90 userName.font = UIFont.systemFont(ofSize: 28, weight: UIFontWeightHeavy)
91+ userName.textColor = UIColor.darkText
61 userName.sizeToFit()
92 userName.sizeToFit()
62 userView.addArrangedSubview(.view(userName))
93 userView.addArrangedSubview(.view(userName))
63− userView.frame.origin = CGPoint(x: 16, y: 16)
94+
95+ userView.layoutIfNeeded()
64
96
65 largeStack.addArrangedSubview(.view(userView))
97 largeStack.addArrangedSubview(.view(userView))
66
98
67− largeStack.addArrangedSubview(.spacer(16))
99+ largeStack.addArrangedSubview(.spacer(vPadding1))
100+
101+
102+ let width = max(minWidth - 2 * padding.x, userView.frame.width)
103+
104+
105+ let photoView = CollectionView()
106+ photoView.alignment = .top
107+ photoView.horizontalPadding = 8
108+ photoView.verticalPadding = 8
109+ photoView.padding = CGPoint.zero
110+ photoView.maxWidth = width
111+ var maxPhotoDim: CGFloat = maxAttachmentDim
112+ if let attachments = post?.attachments {
113+ for attachment in attachments {
114+ if let v = attachment.value {
115+ switch v {
116+ case .photo(let photo):
117+ if var pWidth = photo.width.value >>> {CGFloat($0)},
118+ var pHeight = photo.height.value >>> {CGFloat($0)} {
119+ let photoV = UIImageView()
120+ photoV.layer.minificationFilter = kCAFilterTrilinear
121+ photoV.contentMode = .scaleAspectFill
122+ photo.image?.load(into: photoV)
123+ var k: CGFloat!
124+ if pWidth > pHeight {
125+ k = CGFloat(pWidth) / maxPhotoDim
126+ } else {
127+ k = CGFloat(pHeight) / maxPhotoDim
128+ }
129+ pWidth /= k
130+ pHeight /= k
131+ photoV.frame.size = CGSize(width: pWidth, height: pHeight)
132+ photoView.addArrangedSubview(photoV)
133+ }
134+
135+ default:
136+ break // TODO: Handle other data types
137+ }
138+ }
139+ }
140+ }
141+ photoView.layoutIfNeeded()
142+
143+ var textHeight = height
144+ textHeight -= padding.y * 2 + vPadding1 + userView.frame.height
145+ if let count = post?.attachments.count{
146+ if count > 0 {
147+ textHeight -= vPadding2 + photoView.frame.height
148+ }
149+ }
150+ print(textHeight)
151+ textHeight = max(textHeight, minTextHeight)
152+ print(textHeight)
68
153
69 let body = UILabel()
154 let body = UILabel()
70 body.numberOfLines = 0
155 body.numberOfLines = 0
71− let bodyWidth: Double = 300
156+ let bodyWidth: Double = Double(width)
72 body.font = UIFont.systemFont(ofSize: 22, weight: UIFontWeightSemibold)
157 body.font = UIFont.systemFont(ofSize: 22, weight: UIFontWeightSemibold)
73− body.text = post?.body ?? "Spicy jalapeno flank landjaeger sausage tongue tail fatback frankfurter. Meatball tongue tail short ribs pastrami. Short ribs chicken pig tenderloin sausage pork. Corned beef t-bone pancetta jerky chuck picanha venison, turducken tenderloin shoulder boudin drumstick hamburger. Pork meatball meatloaf prosciutto pig andouille. Sirloin bacon hamburger short ribs pork cupim shank biltong turducken bresaola kevin prosciutto shoulder meatball. Strip steak frankfurter pork chop biltong jowl beef rump venison pancetta turkey tongue."
158+ body.text = post?.body
159+ body.textColor = UIColor.darkText
74 let size = body.textRect(forBounds: CGRect(x: 0, y: 0, width: bodyWidth, height: Double.infinity), limitedToNumberOfLines: 0).size
160 let size = body.textRect(forBounds: CGRect(x: 0, y: 0, width: bodyWidth, height: Double.infinity), limitedToNumberOfLines: 0).size
75− body.frame.size = size
161+ body.frame.size.width = size.width
162+ if size.height > textHeight {
163+ body.frame.size.height = textHeight
164+ let maskView = UIView()
165+ let maskLayer = CAGradientLayer()
166+ maskLayer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor]
167+ maskLayer.endPoint = CGPoint(x: 0.5, y: 1 - (hiddenByGradient/textHeight))
168+ let minPoint = minGradientAmount / textHeight
169+ maskLayer.startPoint = CGPoint(x: 0.5, y: 1 - max(minPoint, gradientRatio))
170+ print(maskLayer.startPoint)
171+ maskLayer.frame = body.bounds
172+ maskView.frame = body.bounds
173+ maskView.layer.addSublayer(maskLayer)
174+ body.mask = maskView
175+ } else {
176+ body.frame.size.height = size.height
177+ }
76
178
77 largeStack.addArrangedSubview(.view(body))
179 largeStack.addArrangedSubview(.view(body))
78
180
181+ if let count = post?.attachments.count{
182+ if count > 0 {
183+ largeStack.addArrangedSubview(.spacer(vPadding2))
184+ largeStack.addArrangedSubview(.view(photoView))
185+ }
186+ }
187+
188+ largeStack.layoutIfNeeded()
189+
190+ frame.size = CGSize(width: width + padding.x * 2, height: min(height, largeStack.frame.height))
191+
192+ gradient.frame = self.bounds
193+ gradient.endPoint = CGPoint(x: 0.5, y: bounds.height / height)
194+
79 }
195 }
80}
196}
Melk/CollectionView.swift+4−2
@@ -68,7 +68,7 @@ class CollectionView: UIView {
68 var firstStack = true
68 var firstStack = true
69
69
70 for view in arrangedSubviews {
70 for view in arrangedSubviews {
71− print("new view")
71+// print("new view")
72 if firstView {
72 if firstView {
73 lastStack.addArrangedSubview(.view(view))
73 lastStack.addArrangedSubview(.view(view))
74 firstView = false
74 firstView = false
@@ -94,7 +94,9 @@ class CollectionView: UIView {
94 }
94 }
95 }
95 }
96 lastStack.layoutIfNeeded()
96 lastStack.layoutIfNeeded()
97+ if !firstStack {
97− largeStack.addArrangedSubview(.spacer(verticalPadding))
98+ largeStack.addArrangedSubview(.spacer(verticalPadding))
99+ }
98 largeStack.addArrangedSubview(.view(lastStack))
100 largeStack.addArrangedSubview(.view(lastStack))
99 largeStack.frame.origin = CGPoint.zero
101 largeStack.frame.origin = CGPoint.zero
100 largeStack.layoutIfNeeded()
102 largeStack.layoutIfNeeded()
Melk/Info.plist+18
@@ -16,8 +16,26 @@
16 <string>APPL</string>
16 <string>APPL</string>
17 <key>CFBundleShortVersionString</key>
17 <key>CFBundleShortVersionString</key>
18 <string>1.0</string>
18 <string>1.0</string>
19+ <key>CFBundleURLTypes</key>
20+ <array>
21+ <dict>
22+ <key>CFBundleTypeRole</key>
23+ <string>Editor</string>
24+ <key>CFBundleURLName</key>
25+ <string>com.ilyakooo0.Melk</string>
26+ <key>CFBundleURLSchemes</key>
27+ <array>
28+ <string>vk6097091</string>
29+ </array>
30+ </dict>
31+ </array>
19 <key>CFBundleVersion</key>
32 <key>CFBundleVersion</key>
20 <string>1</string>
33 <string>1</string>
34+ <key>LSApplicationQueriesSchemes</key>
35+ <array>
36+ <string>vkauthorize</string>
37+ <string>vk6097091</string>
38+ </array>
21 <key>LSRequiresIPhoneOS</key>
39 <key>LSRequiresIPhoneOS</key>
22 <true/>
40 <true/>
23 <key>UILaunchStoryboardName</key>
41 <key>UILaunchStoryboardName</key>
Melk/LocalVKDataTypes.swift+137−91
@@ -11,7 +11,8 @@ import RealmSwift
11import Alamofire
11import Alamofire
12import CoreLocation
12import CoreLocation
13import Nuke
13import Nuke
14+import SwiftyJSON
14−import SwiftyVK
15+//import SwiftyVK
15
16
16
17
17typealias Token = String
18typealias Token = String
@@ -292,17 +293,25 @@ class User: Object {
292 }
293 }
293 }
294 }
294
295
296+// func get(completion: ((User?) -> ())? = nil) {
297+// VK.API.Users.get([VK.Arg.userIDs: "\(id)"]).send(onSuccess: { (response) in
298+// if let json = response.array?.first {
299+// let us = VKData.processUser(json: json)
300+// completion?(us)
301+// } else {
302+// completion?(nil)
303+// }
304+// }, onError: { (error) in
305+// completion?(nil)
306+// })
307+// }
295 func get(completion: ((User?) -> ())? = nil) {
308 func get(completion: ((User?) -> ())? = nil) {
296− VK.API.Users.get([VK.Arg.userIDs: "\(id)"]).send(onSuccess: { (response) in
309+ VKServer.get(user: self) { (data) in
297− if let json = response.array?.first {
310+ if let json = JSON.init(data: data)["response"].array?.first {
298− let us = VKData.processUser(json: json)
311+ let user = VKData.processUser(json: json)
299− completion?(us)
312+ completion?(user)
300− } else {
301− completion?(nil)
302 }
313 }
303− }, onError: { (error) in
314+ }
304− completion?(nil)
305− })
306 }
315 }
307
316
308
317
@@ -493,19 +502,19 @@ class Message: GenericMessage, Comparable, SortedArrayItem {
493 }
502 }
494 }
503 }
495
504
496− func get(completion: ((Message?) -> ())? = nil) {
505+// func get(completion: ((Message?) -> ())? = nil) {
497− VK.API.Messages.getById([VK.Arg.messageIds: String(id)]).send(onSuccess: { (response) in
506+// VK.API.Messages.getById([VK.Arg.messageIds: String(id)]).send(onSuccess: { (response) in
498− if let json = response.array?.first {
507+// if let json = response.array?.first {
499− let mes = VKData.processMessage(response)
508+// let mes = VKData.processMessage(response)
500− completion?(mes)
509+// completion?(mes)
501− } else {
510+// } else {
502− completion?(nil)
511+// completion?(nil)
503− }
512+// }
504− }, onError: { (error) in
513+// }, onError: { (error) in
505− completion?(nil)
514+// completion?(nil)
506− })
515+// })
507− // TODO: Implement propperly?
516+// // TODO: Implement propperly?
508− }
517+// }
509
518
510 class func by(id: MessageID) -> Message {
519 class func by(id: MessageID) -> Message {
511 let realm = try! Realm()
520 let realm = try! Realm()
@@ -1239,24 +1248,32 @@ class Photo: Object {
1239 }
1248 }
1240 }
1249 }
1241
1250
1251+// func get(completion: ((Photo?) -> ())? = nil) {
1252+// if let ownerID = owner?.id {
1253+// var ph = "\(ownerID)_\(id)"
1254+// if let key = accessKey {
1255+// ph.append("_\(key)")
1256+// }
1257+// VK.API.Photos.getById([VK.Arg.photos: ph]).send(onSuccess: { (response) in
1258+// if let json = response.array?.first {
1259+// let photo = VKData.processPhoto(json)
1260+// completion?(photo)
1261+// } else {
1262+// completion?(nil)
1263+// }
1264+// }, onError: { (error) in
1265+// completion?(nil)
1266+// })
1267+// } else {
1268+// completion?(nil)
1269+// }
1270+// }
1242 func get(completion: ((Photo?) -> ())? = nil) {
1271 func get(completion: ((Photo?) -> ())? = nil) {
1243− if let ownerID = owner?.id {
1272+ VKServer.get(photo: self) { data in
1244− var ph = "\(ownerID)_\(id)"
1273+ if let json = JSON.init(data: data)["response"].array?.first {
1245− if let key = accessKey {
1274+ let photo = VKData.processPhoto(json)
1246− ph.append("_\(key)")
1275+ completion?(photo)
1247 }
1276 }
1248− VK.API.Photos.getById([VK.Arg.photos: ph]).send(onSuccess: { (response) in
1249− if let json = response.array?.first {
1250− let photo = VKData.processPhoto(json)
1251− completion?(photo)
1252− } else {
1253− completion?(nil)
1254− }
1255− }, onError: { (error) in
1256− completion?(nil)
1257− })
1258− } else {
1259− completion?(nil)
1260 }
1277 }
1261 }
1278 }
1262
1279
@@ -1366,33 +1383,42 @@ class WallPost: Object {
1366 // self.copyOwnerID = copyOwnerID
1383 // self.copyOwnerID = copyOwnerID
1367 // }
1384 // }
1368
1385
1386+// func get(completion: ((WallPost?) -> ())? = nil) {
1387+// if let ownerID = owner?.id {
1388+// var ph = "\(ownerID)_\(id)"
1389+// // if let key = accessKey {
1390+// // ph.append("_\(key)")
1391+// // }
1392+// VK.API.Wall.getById([VK.Arg.posts: ph]).send(onSuccess: { (response) in
1393+// if let json = response.array?.first {
1394+// let photo = VKData.processWallPost(json)
1395+// completion?(photo)
1396+// } else {
1397+// completion?(nil)
1398+// }
1399+// }, onError: { (error) in
1400+// completion?(nil)
1401+// })
1402+// } else {
1403+// completion?(nil)
1404+// }
1405+// }
1369 func get(completion: ((WallPost?) -> ())? = nil) {
1406 func get(completion: ((WallPost?) -> ())? = nil) {
1370− if let ownerID = owner?.id {
1407+ VKServer.get(wallPost: self) { (data) in
1371− var ph = "\(ownerID)_\(id)"
1408+ if let json = JSON.init(data: data)["response"].array?.first {
1372− // if let key = accessKey {
1373− // ph.append("_\(key)")
1374− // }
1375− VK.API.Wall.getById([VK.Arg.posts: ph]).send(onSuccess: { (response) in
1376− if let json = response.array?.first {
1377− let photo = VKData.processWallPost(json)
1409+ let post = VKData.processWallPost(json)
1378− completion?(photo)
1379− } else {
1380− completion?(nil)
1410+ completion?(post)
1381− }
1382− }, onError: { (error) in
1383− completion?(nil)
1384− })
1411+ }
1385− } else {
1386− completion?(nil)
1387 }
1412 }
1388 }
1413 }
1414+
1389
1415
1390
1416
1391 override class func primaryKey() -> String? {
1417 override class func primaryKey() -> String? {
1392 return "id"
1418 return "id"
1393 }
1419 }
1394
1420
1395− class func by(id: UserID) -> WallPost {
1421+ class func by(id: WallPostID) -> WallPost {
1396 let realm = try! Realm()
1422 let realm = try! Realm()
1397 if let us = realm.object(ofType: WallPost.self, forPrimaryKey: id) {
1423 if let us = realm.object(ofType: WallPost.self, forPrimaryKey: id) {
1398 return us
1424 return us
@@ -1432,6 +1458,10 @@ class WallPostReply: Object {
1432 }
1458 }
1433 }
1459 }
1434
1460
1461+ func get(completion: (() -> ())) {
1462+ // TODO: Get wallPostReply
1463+ }
1464+
1435 override class func primaryKey() -> String? {
1465 override class func primaryKey() -> String? {
1436 return "id"
1466 return "id"
1437 }
1467 }
@@ -1712,24 +1742,32 @@ class Video: Object {
1712 }
1742 }
1713 }
1743 }
1714
1744
1745+// func get(completion: ((Video?) -> ())? = nil) {
1746+// if let peerID = owner?.id {
1747+// var vi = "\(peerID)_\(id)"
1748+// if let key = accessKey {
1749+// vi.append("_\(key)")
1750+// }
1751+// VK.API.Video.get([VK.Arg.videos: vi]).send(onSuccess: { (response) in
1752+// if let json = response["items"].array?.first {
1753+// let video = VKData.processVideo(json)
1754+// completion?(video)
1755+// } else {
1756+// completion?(nil)
1757+// }
1758+// }, onError: { (error) in
1759+// completion?(nil)
1760+// })
1761+// } else {
1762+// completion?(nil)
1763+// }
1764+// }
1715 func get(completion: ((Video?) -> ())? = nil) {
1765 func get(completion: ((Video?) -> ())? = nil) {
1716− if let peerID = owner?.id {
1766+ VKServer.get(video: self) { (data) in
1717− var vi = "\(peerID)_\(id)"
1767+ if let json = JSON.init(data: data)["response"].array?.first {
1718− if let key = accessKey {
1768+ let video = VKData.processVideo(json)
1719− vi.append("_\(key)")
1769+ completion?(video)
1720 }
1770 }
1721− VK.API.Video.get([VK.Arg.videos: vi]).send(onSuccess: { (response) in
1722− if let json = response["items"].array?.first {
1723− let video = VKData.processVideo(json)
1724− completion?(video)
1725− } else {
1726− completion?(nil)
1727− }
1728− }, onError: { (error) in
1729− completion?(nil)
1730− })
1731− } else {
1732− completion?(nil)
1733 }
1771 }
1734 }
1772 }
1735
1773
@@ -1836,24 +1874,32 @@ class Document: Object {
1836 }
1874 }
1837 }
1875 }
1838
1876
1877+// func get(completion: ((Document?) -> ())? = nil) {
1878+// if let ownerID = owner?.id {
1879+// var doc = "\(ownerID)_\(id)"
1880+// // if let key = accessKey {
1881+// // doc.append("_\(key)")
1882+// // }
1883+// VK.API.Docs.getById([VK.Arg.docs: doc]).send(onSuccess: { response in
1884+// if let json = response.array?.first {
1885+// let document = VKData.processDocument(json)
1886+// completion?(document)
1887+// } else {
1888+// completion?(nil)
1889+// }
1890+// }, onError: { (error) in
1891+// completion?(nil)
1892+// })
1893+// } else {
1894+// completion?(nil)
1895+// }
1896+// }
1839 func get(completion: ((Document?) -> ())? = nil) {
1897 func get(completion: ((Document?) -> ())? = nil) {
1840− if let ownerID = owner?.id {
1898+ VKServer.get(doc: self) { (data) in
1841− var doc = "\(ownerID)_\(id)"
1899+ if let json = JSON.init(data: data)["response"].array?.first {
1842− // if let key = accessKey {
1843− // doc.append("_\(key)")
1844− // }
1845− VK.API.Docs.getById([VK.Arg.docs: doc]).send(onSuccess: { response in
1846− if let json = response.array?.first {
1847− let document = VKData.processDocument(json)
1900+ let doc = VKData.processDocument(json)
1848− completion?(document)
1849− } else {
1850− completion?(nil)
1901+ completion?(doc)
1851− }
1852− }, onError: { (error) in
1853− completion?(nil)
1854− })
1902+ }
1855− } else {
1856− completion?(nil)
1857 }
1903 }
1858 }
1904 }
1859
1905
@@ -2053,7 +2099,7 @@ class Multichat: Object {
2053 if let us = realm.object(ofType: Multichat.self, forPrimaryKey: id) {
2099 if let us = realm.object(ofType: Multichat.self, forPrimaryKey: id) {
2054 return us
2100 return us
2055 } else {
2101 } else {
2056− VkServices.getMultichat(for: id)
2102+// VkServices.getMultichat(for: id)
2057 let us = Multichat()
2103 let us = Multichat()
2058 realmWrite { realm in
2104 realmWrite { realm in
2059 us.id = id
2105 us.id = id
Melk/MainCoordinator.swift+86−1
@@ -7,20 +7,105 @@
7//
7//
8
8
9import UIKit
9import UIKit
10+import RealmSwift
11+//import SwiftyVK
10
12
11class MainCoordinator {
13class MainCoordinator {
12 let window: UIWindow
14 let window: UIWindow
13 let mainVC = MainViewController()
15 let mainVC = MainViewController()
16+ let loadingVC = UIViewController() // TODO: An actual loading VC
14 let navC: UINavigationController!
17 let navC: UINavigationController!
15 let secondaryCoord = SecondaryCoordinator()
18 let secondaryCoord = SecondaryCoordinator()
19+ let stream = StreamingService()
16 init(window: UIWindow) {
20 init(window: UIWindow) {
17 self.window = window
21 self.window = window
22+ window.rootViewController = loadingVC
23+
24+ Realm.Configuration.defaultConfiguration = Realm.Configuration
25+ .init(inMemoryIdentifier: "com.ilyakooo0.Melk.realm",
26+ readOnly: false,
27+ deleteRealmIfMigrationNeeded: true)
28+
18 navC = UINavigationController(rootViewController: mainVC)
29 navC = UINavigationController(rootViewController: mainVC)
19− window.rootViewController = navC
30+
31+ mainVC.present = { tags in
32+ if tags.count > 0 {
33+ self.secondaryCoord.present(tag: tags.first!)
34+ self.stream.rules = tags
35+ self.stream.connect()
36+ }
37+ }
20 updateScreens()
38 updateScreens()
39+ stream.handleResult = { result in
40+ self.secondaryCoord.add(result: result)
41+// switch result {
42+// case .post(let post):
43+//
44+// }
45+ }
46+ }
47+
48+ func start() {
49+// VK.configure(withAppId: appID, delegate: self)
50+// if VK.state != .authorized {
51+// VK.logIn()
52+// }
53+ print("didAuthorize")
54+ window.rootViewController = navC
21 }
55 }
22
56
23 func updateScreens() {
57 func updateScreens() {
24 secondaryCoord.updateScreens()
58 secondaryCoord.updateScreens()
25 }
59 }
26}
60}
61+/*
62+extension MainCoordinator: VKDelegate {
63+ /** ---DEPRECATED. TOKEN NOW STORED IN KEYCHAIN--- Called when SwiftyVK need know where a token is located
64+ - returns: Path to save/read token or nil if should save token to UserDefaults*/
65+ func vkShouldUseTokenPath() -> String? {
66+ return nil
67+ }
68+
69+ func vkWillAuthorize() -> Set<VK.Scope> {
70+ //Called when SwiftyVK need authorization permissions.
71+ return permissions
72+ }
73+
74+ func vkDidAuthorizeWith(parameters: Dictionary<String, String>) {
75+ //Called when the user is log in.
76+ //Here you can start to send requests to the API.
77+ print("authorized")
78+ mainSync {
79+ self.window.rootViewController = self.navC
80+ }
81+ authorized()
82+ }
83+
84+ func vkAutorizationFailedWith(error: AuthError) {
85+ //Called when SwiftyVK could not authorize. To let the application know that something went wrong.
86+ print("failed auth with error \(error.errorCode): \(error.description)")
87+ mainSync {
88+ self.window.rootViewController = self.navC
89+ }
90+ }
91+
92+ func vkDidUnauthorize() {
93+ print("did unauthorize")
94+ mainSync {
95+ self.window.rootViewController = self.navC
96+ }
97+ //Called when user is log out.
98+ }
99+
100+ func vkWillPresentView() -> UIViewController {
101+ //Only for iOS!
102+ //Called when need to display a view from SwiftyVK.
103+ let authController = UIViewController()
104+ mainSync {
105+ self.window.rootViewController = authController
106+ }
107+ return authController //UIViewController that should present authorization view controller
108+ }
109+
110+}
111+ */
Melk/MainViewController.swift+26−4
@@ -10,7 +10,9 @@ import UIKit
10
10
11class MainViewController: UIViewController {
11class MainViewController: UIViewController {
12
12
13+ var present: (([String]) -> ())?
14+
13− let mainView = MainView()
15+ private let mainView = MainView()
14
16
15 init() {
17 init() {
16 super.init(nibName: nil, bundle: nil)
18 super.init(nibName: nil, bundle: nil)
@@ -26,8 +28,10 @@ class MainViewController: UIViewController {
26 fatalError("init(coder:) has not been implemented")
28 fatalError("init(coder:) has not been implemented")
27 }
29 }
28
30
29− func presentTags() {
31+ @objc private func presentTags() {
30−
32+ present?(
33+ mainView.tags.flatMap({$0}).filter({$0 != ""})
34+ )
31 }
35 }
32}
36}
33
37
@@ -43,6 +47,8 @@ class MainView: UIView {
43 tableView.rowHeight = UITableViewAutomaticDimension
47 tableView.rowHeight = UITableViewAutomaticDimension
44 tableView.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
48 tableView.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
45 tableView.register(TagTableViewCell.self, forCellReuseIdentifier: TagTableViewCell.identifier)
49 tableView.register(TagTableViewCell.self, forCellReuseIdentifier: TagTableViewCell.identifier)
50+ tableView.separatorStyle = .none
51+ tableView.allowsSelection = false
46 setNeedsLayout()
52 setNeedsLayout()
47 }
53 }
48
54
@@ -50,6 +56,17 @@ class MainView: UIView {
50 tableView.frame = bounds
56 tableView.frame = bounds
51 }
57 }
52
58
59+ fileprivate var tags: [String?] = [nil] {
60+ didSet {
61+ if tags.last! != nil && tags.last! != "" {
62+ if tags.count < 10 {
63+ tags.append(nil)
64+ tableView.insertRows(at: [IndexPath.init(row: tags.count - 1, section: 0)], with: .automatic)
65+ }
66+ }
67+ }
68+ }
69+
53 required init?(coder aDecoder: NSCoder) {
70 required init?(coder aDecoder: NSCoder) {
54 fatalError("init(coder:) has not been implemented")
71 fatalError("init(coder:) has not been implemented")
55 }
72 }
@@ -60,13 +77,18 @@ extension MainView: UITableViewDataSource {
60 if let cell = tableView.dequeueReusableCell(withIdentifier: TagTableViewCell.identifier) as? TagTableViewCell {
77 if let cell = tableView.dequeueReusableCell(withIdentifier: TagTableViewCell.identifier) as? TagTableViewCell {
61// cell.titleText = "Testing testing 1 2 3"
78// cell.titleText = "Testing testing 1 2 3"
62 cell.style = indexPath.item == 0 ? .large : .normal
79 cell.style = indexPath.item == 0 ? .large : .normal
80+ let index = indexPath.row
81+ cell.tagDidChange = { tag in
82+ self.tags[index] = tag
83+ }
84+ cell.tagString = tags[index]
63 return cell
85 return cell
64 }
86 }
65 return UITableViewCell()
87 return UITableViewCell()
66 }
88 }
67
89
68 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
90 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
69− return 3
91+ return tags.count
70 }
92 }
71
93
72 func numberOfSections(in tableView: UITableView) -> Int {
94 func numberOfSections(in tableView: UITableView) -> Int {
Melk/PublicCrap.swift+10
@@ -247,3 +247,13 @@ struct DecayingAverage {
247 value = (value * weight + num) / (weight + 1)
247 value = (value * weight + num) / (weight + 1)
248 }
248 }
249}
249}
250+
251+func mainSync(_ code: (() -> ())) {
252+ if Thread.isMainThread {
253+ code()
254+ } else {
255+ DispatchQueue.main.sync {
256+ code()
257+ }
258+ }
259+}
Melk/SecondaryCoordinator.swift+24−1
@@ -12,19 +12,42 @@ class SecondaryCoordinator {
12
12
13 var windows: [UIWindow] = []
13 var windows: [UIWindow] = []
14
14
15+ private var didStartPresenting = false
16+ private var tag: String = ""
17+
18+ func add(result: StreamingServiceResult) {
19+ for vc in VCs {
20+ vc.add(result: result)
21+ }
22+ }
23+
24+ func present(tag newTag: String) {
25+ didStartPresenting = true
26+ self.tag = newTag
27+ VCs.map({ $0.present(tag: newTag) })
28+ }
29+
15 init() {
30 init() {
16
31
17 }
32 }
18
33
34+ private var VCs: [SecondaryViewController] = []
35+
19 func updateScreens() {
36 func updateScreens() {
20 windows.removeAll()
37 windows.removeAll()
38+ VCs.removeAll()
21 for screen in UIScreen.screens.dropFirst() {
39 for screen in UIScreen.screens.dropFirst() {
22 let window = UIWindow(frame: screen.bounds)
40 let window = UIWindow(frame: screen.bounds)
23 window.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
41 window.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
24 window.screen = screen
42 window.screen = screen
25 window.isHidden = false
43 window.isHidden = false
44+ let VC = SecondaryViewController()
45+ VCs.append(VC)
26− window.rootViewController = SecondaryViewController()
46+ window.rootViewController = VC
27 windows.append(window)
47 windows.append(window)
48+ if didStartPresenting {
49+ VC.present(tag: tag)
50+ }
28 }
51 }
29 }
52 }
30}
53}
Melk/SecondaryViewController.swift+125−9
@@ -9,29 +9,145 @@
9import UIKit
9import UIKit
10
10
11class SecondaryViewController: UIViewController {
11class SecondaryViewController: UIViewController {
12+
12 init() {
13 init() {
13 super.init(nibName: nil, bundle: nil)
14 super.init(nibName: nil, bundle: nil)
14− self.view = SecondaryView(frame: view.bounds)
15+ self.view = sView
16+ }
17+
18+ func add(result: StreamingServiceResult) {
19+ sView.add(result: result)
20+ }
21+
22+ func present(tag: String) {
23+ sView.present(tag: tag)
15 }
24 }
16
25
17 required init?(coder aDecoder: NSCoder) {
26 required init?(coder aDecoder: NSCoder) {
18 fatalError("init(coder:) has not been implemented")
27 fatalError("init(coder:) has not been implemented")
19 }
28 }
29+
30+ private let sView = SecondaryView()
20}
31}
21
32
22class SecondaryView: UIView {
33class SecondaryView: UIView {
34+
35+ var mainTag: String? {
36+ get {
37+ return tagLabel.text
38+ }
39+ set {
40+ tagLabel.text = newValue
41+
42+ }
43+ }
44+
45+
46+ private let spacer: CGFloat = 32
47+ private let bottomSpacer: CGFloat = 64
48+ private let tagSpacer: CGFloat = 32
49+ private let tagLabel = UILabel()
50+ private let melkLabel = UILabel()
51+
52+ func present(tag: String) {
53+ UIView.perform(.delete, on: [melkLabel], options: [], animations: nil) { (_) in
54+ self.melkLabel.removeFromSuperview()
55+ self.mainTag = tag
56+ self.tagLabel.sizeToFit()
57+ self.layoutIfNeeded(animated: true)
58+ }
59+ }
60+
61+ func add(result: StreamingServiceResult) {
62+ switch result {
63+ case .post(let post):
64+ let card = CardContainer()
65+ card.minWidth = min(bounds.width / 2.3, 550)
66+ card.height = cardHeight
67+ card.post = post
68+ cards.addArrangedSubview(.spacer(spacer))
69+ cards.addArrangedSubview(.view(card))
70+ card.animate()
71+ }
72+ self.layoutIfNeeded(animated: true)
73+// self.setNeedsLayout(animated: true) // Doesn't work for some reason
74+ }
75+
76+// private let card = CardView()
77+ private let cards = StackView(axis: .horizontal, alignment: .top)
78+
23 override init(frame: CGRect) {
79 override init(frame: CGRect) {
24 super.init(frame: frame)
80 super.init(frame: frame)
81+ backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
82+ addSubview(cards)
83+
84+ melkLabel.text = "Melk"
85+ melkLabel.font = UIFont.systemFont(ofSize: 200, weight: UIFontWeightHeavy)
86+ melkLabel.textColor = #colorLiteral(red: 0.9601849914, green: 0.9601849914, blue: 0.9601849914, alpha: 1)
87+ addSubview(melkLabel)
88+
89+ tagLabel.font = UIFont.systemFont(ofSize: 69, weight: UIFontWeightBold)
90+ tagLabel.textColor = #colorLiteral(red: 0.3176470588, green: 0.3179988265, blue: 0.3179988265, alpha: 1)
91+ addSubview(tagLabel)
92+ }
93+
94+ func setNeedsLayout(animated: Bool) {
95+ self.animate = animated
96+ self.setNeedsLayout()
97+ }
98+ func layoutIfNeeded(animated: Bool) {
99+ self.animate = animated
100+ self.layoutIfNeeded()
101+ }
102+ private var animate: Bool = false
103+
104+ private func cleanCards() {
105+ var offset = 0
106+ outLoop: for (i, element) in cards.arrangedSubviews.enumerated() {
107+ switch element {
108+ case .view(let card):
109+ let cardFrame = convert(card.frame, from: cards)
110+ if cardFrame.intersects(bounds) {
111+ break outLoop
112+ } else {
113+ cards.arrangedSubviews.remove(at: i-1-offset)
114+ cards.arrangedSubviews.remove(at: i-1-offset)
115+ offset += 2
116+ }
117+ case .spacer(_):
118+ break
119+ }
120+ }
121+ }
122+
123+ private let padding = CGPoint(x: 32, y: 16)
124+ private var cardHeight: CGFloat = 400
125+
126+ override func layoutSubviews() {
127+ super.layoutSubviews()
25
128
26− let card = CardView(frame: CGRect(x: 20, y: 20, width: 600, height: 300))
129+ melkLabel.sizeToFit()
27− let post = WallPost()
130+ melkLabel.center = center
28− post.owner = User()
29− post.owner?.firstName = "David"
30− post.owner?.avatar = RealmPhoto()
31− post.owner?.avatar?.imageSURL = "https://static.independent.co.uk/s3fs-public/styles/article_small/public/thumbnails/image/2017/04/01/17/twitter-egg.jpg"
32− card.post = post
33− addSubview(card)
34
131
132+ let updates = {
133+ self.tagLabel.sizeToFit()
134+ self.tagLabel.center.x = self.center.x
135+ self.tagLabel.frame.origin.y = self.tagSpacer
136+ self.cards.frame.origin.y = self.tagLabel.frame.maxY + self.padding.y
137+ self.cards.layoutIfNeeded()
138+ self.cards.frame.origin.x = self.frame.width - self.padding.x - self.cards.frame.width
139+ }
140+ if animate {
141+ UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.6, options: [], animations: updates, completion: { _ in
142+ self.cleanCards()
143+ })
144+// UIView.animate(withDuration: 0.4, delay: 0.0, options: [.curveEaseOut], animations: updates, completion: nil)
145+ } else {
146+ updates()
147+ self.cleanCards()
148+ }
149+ cardHeight = bounds.height - tagLabel.frame.height - padding.y - tagSpacer - bottomSpacer
150+ animate = false
35 }
151 }
36
152
37 required init?(coder aDecoder: NSCoder) {
153 required init?(coder aDecoder: NSCoder) {
Melk/StackView.swift+3−1
@@ -44,6 +44,8 @@ class StackView: UIView {
44 super.layoutSubviews()
44 super.layoutSubviews()
45 print("laying out subviews")
45 print("laying out subviews")
46
46
47+ subviews.map({$0.removeFromSuperview()}) // TODO: There must be a better way!
48+
47 var height: CGFloat = 0
49 var height: CGFloat = 0
48 var width: CGFloat = 0
50 var width: CGFloat = 0
49 switch axis {
51 switch axis {
@@ -62,7 +64,7 @@ class StackView: UIView {
62 height += a
64 height += a
63 }
65 }
64 case .view(let view):
66 case .view(let view):
65−
67+ view.layoutIfNeeded()
66 switch axis {
68 switch axis {
67 case .horizontal:
69 case .horizontal:
68 view.frame.origin.x = width
70 view.frame.origin.x = width
Melk/Streaming.swiftnew file+195
@@ -0,0 +1,195 @@
1+//
2+// Streaming.swift
3+// Melk
4+//
5+// Created by Ilya Kos on 7/9/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+import SwiftWebSocket
11+import Alamofire
12+import SwiftyJSON
13+
14+class Streaming {
15+
16+ let streamingServerSurl = "https://api.vk.com/method/streaming.getServerUrl"
17+ let params: [String: String] = [
18+ "access_token": accessKey,
19+ "v": vkAPIVersion
20+ ]
21+
22+ private var key: String?
23+ private var server: String?
24+ let ws = WebSocket()
25+
26+ var processItem: ((Any) -> ())?
27+
28+ func getServer(completion: (() -> ())? = nil) {
29+ Alamofire.request(streamingServerSurl, parameters: params).validate().responseData { (response) in
30+ switch response.result {
31+ case .success(let data):
32+ let json = JSON.init(data: data)["response"]
33+ self.key = json["key"].string
34+ self.server = json["endpoint"].string
35+ case .failure(let error):
36+ print(error)
37+ }
38+ DispatchQueue.main.async {
39+ completion?()
40+ }
41+ }
42+ }
43+
44+ func add(rule: Rule, completion: (() -> ())? = nil) {
45+ if let key = key,
46+ let server = server {
47+ let url = "https://\(server)/rules?key=\(key)"
48+ let request: [String: Any] = [
49+ "rule": [
50+ "value": rule.rule,
51+ "tag": rule.tag
52+ ]
53+ ]
54+ print(try? JSONSerialization.data(withJSONObject: request, options: .prettyPrinted) =>> {String.init(data: $0, encoding: .utf8)})
55+ Alamofire.request(url, method: .post, parameters: request, encoding: JSONEncoding.default)
56+ .validate().responseData(completionHandler: { (response) in
57+ switch response.result {
58+ case .success(let data):
59+ let json = JSON.init(data: data)
60+ print(json) // TODO: Handle error
61+ case .failure(let error):
62+ print(error)
63+ }
64+ DispatchQueue.main.async {
65+ completion?()
66+ }
67+ })
68+ } else {
69+ getServer() {
70+ self.add(rule: rule, completion: completion)
71+ }
72+ }
73+ }
74+ func add(rules: [Rule], completion: (() -> ())? = nil) {
75+ if let server = server,
76+ let key = key {
77+ var numOfCompletions = rules.count + 1
78+ let complete: (() -> ()) = {
79+ numOfCompletions -= 1
80+ if numOfCompletions == 0 {
81+ completion?()
82+ }
83+ }
84+
85+ complete()
86+
87+ for rule in rules {
88+ add(rule: rule, completion: complete)
89+ }
90+ } else {
91+ getServer() {
92+ self.add(rules: rules, completion: completion)
93+ }
94+ }
95+ }
96+ func getRules(completion: @escaping (([Rule]?) -> ())) {
97+ if let server = server,
98+ let key = key {
99+ let url = "https://\(server)/rules?key=\(key)"
100+ Alamofire.request(url, method: .get).validate().responseData { (response) in
101+ switch response.result {
102+ case .success(let data):
103+ let json = JSON.init(data: data)
104+ print(json)
105+ if json["code"].int == 200 {
106+ var resultRules: [Rule] = []
107+ if let rules = json["rules"].array {
108+ for rule in rules {
109+ if let tag = rule["tag"].string,
110+ let value = rule["value"].string {
111+ resultRules.append((rule: value, tag: tag))
112+ }
113+ }
114+ }
115+ completion(resultRules)
116+ } // TODO" Handle errors
117+ case .failure(let error):
118+ print(error) // TODO: HAndl;e error
119+ }
120+ }
121+ } else {
122+ getServer() {
123+ self.getRules(completion: completion)
124+ }
125+ }
126+ }
127+ func remove(rule: Rule, completion: (() -> ())? = nil) {
128+ if let server = server,
129+ let key = key {
130+ let url = "https://\(server)/rules?key=\(key)"
131+ let request: [String: Any] = [
132+ "tag": rule.tag
133+ ]
134+ Alamofire.request(url, method: .delete, parameters: request, encoding: JSONEncoding.default).validate().responseData { response in
135+ switch response.result {
136+ case .success(let data):
137+ let json = JSON.init(data: data)
138+ print(json) // TODO: HAndle error
139+ completion?()
140+ case .failure(let error):
141+ print(error) // TODO: HAndl;e error
142+ }
143+ }
144+ } else {
145+ getServer() {
146+ self.remove(rule: rule, completion: completion)
147+ }
148+ }
149+ }
150+ func removeAllRules(completion: (() -> ())? = nil) {
151+ getRules { (rules) in
152+ if let rules = rules {
153+
154+ var numOfCompleted = rules.count + 1
155+ let complete : (() -> ()) = {
156+ numOfCompleted -= 1
157+ if numOfCompleted == 0 {
158+ completion?()
159+ }
160+ }
161+
162+ complete()
163+
164+ for rule in rules {
165+ self.remove(rule: rule, completion: complete)
166+ }
167+ }
168+ }
169+ }
170+ func connect(completion: (() -> ())? = nil) {
171+ if let key = key,
172+ let server = server {
173+ let surl = "wss://\(server)/stream?key=\(key)"
174+ let url = URL(string: surl)!
175+ ws.event.message = { message in
176+ self.processItem?(message)
177+ }
178+ let request = NSMutableURLRequest(url: url)
179+ request.httpMethod = "GET"
180+ request.addValue("upgrade", forHTTPHeaderField: "Connection")
181+ request.addValue("websocket", forHTTPHeaderField: "Upgrade")
182+ request.addValue("13", forHTTPHeaderField: "Sec-Websocket-Version")
183+ ws.open(request: request as URLRequest)
184+ DispatchQueue.main.async {
185+ completion?()
186+ }
187+ } else {
188+ getServer() {
189+ self.connect(completion: completion)
190+ }
191+ }
192+ }
193+}
194+
195+typealias Rule = (rule: String, tag: String)
Melk/StreamingParser.swiftnew file+80
@@ -0,0 +1,80 @@
1+//
2+// StreamingParser.swift
3+// Melk
4+//
5+// Created by Ilya Kos on 7/12/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+import Decodable
11+
12+class StreamingParser {
13+ static func parse(json: DJSON, completion: @escaping ((StreamingResponse) -> ())) {
14+ do {
15+ let code: Int = try json => "code"
16+ switch code {
17+ case 100:
18+ let event = try json => "event"
19+ let eventType: String = try event => "event_type"
20+ let eventID = try event => "event_id"
21+ let postID: WallPostID = try eventID => "post_id"
22+ let ownerID: UserID = try eventID => "post_owner_id"
23+ // let
24+ var post: WallPost!
25+ var comp = 2
26+ realmWrite { _ in
27+ post = WallPost.by(id: postID)
28+ post.owner = User.by(id: ownerID)
29+ switch eventType {
30+ case "post":
31+ break
32+ case "comment":
33+ /*
34+ if let id: Int = try? eventID => "comment_id" {
35+ let comment = WallPostReply.by(id: id)
36+ comment.wallPost = post
37+ comp += 1
38+ comment // TODO: get comment
39+ }
40+ */
41+ print("comment not implemented")
42+
43+ case "share":
44+ break
45+ default:
46+ print("error:\n\(json)")
47+ }
48+ }
49+// print(post.owner)
50+ let complete = {
51+ comp -= 1
52+ if comp == 0 {
53+// print(post.owner)
54+ completion(.post(post))
55+ }
56+ }
57+ post.owner?.get(completion: { (_) in
58+ complete()
59+ })
60+ post.get(completion: { (_) in
61+ complete()
62+ })
63+ break // handle event
64+ case 300:
65+ break // handle service message
66+ default:
67+ print("Not implemented:\n\(json)\n")
68+ }
69+ } catch let error {
70+ print(error)
71+ }
72+ }
73+}
74+
75+enum StreamingResponse {
76+ case service
77+ case post(WallPost)
78+}
79+
80+typealias DJSON = Any
Melk/StreamingService.swiftnew file+61
@@ -0,0 +1,61 @@
1+//
2+// StreamingService.swift
3+// Melk
4+//
5+// Created by Ilya Kos on 7/12/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+class StreamingService {
12+ init() {
13+ streaming.processItem = { item in
14+ if let string = item as? String,
15+ let data = string.data(using: .utf8),
16+ let json = try? JSONSerialization.jsonObject(with: data, options: []) {
17+ StreamingParser.parse(json: json, completion: { (result) in
18+ switch result {
19+ case .post(let post):
20+ self.handleResult?(.post(post))
21+ case .service:
22+ break // TODO: HAndle servoce message
23+ }
24+ })
25+ }
26+ }
27+ }
28+ var rules: [String] = [] {
29+ didSet {
30+ updateRules()
31+ }
32+ }
33+
34+ func connect() {
35+ streaming.connect()
36+ }
37+
38+ var handleResult: ((StreamingServiceResult) -> ())?
39+
40+
41+
42+ private let streaming = Streaming()
43+
44+ private func updateRules() {
45+ streaming.removeAllRules {
46+ var outRules: [Rule] = []
47+ var i = 1
48+ for rule in self.rules {
49+ if i <= 10 {
50+ outRules.append((rule: rule, tag: String(i)))
51+ i += 1
52+ }
53+ }
54+ self.streaming.add(rules: outRules)
55+ }
56+ }
57+}
58+
59+enum StreamingServiceResult {
60+ case post(WallPost)
61+}
Melk/TableViewCells.swift+12−3
@@ -57,6 +57,12 @@ class TagTableViewCell: UITableViewCell {
57 }
57 }
58 }
58 }
59
59
60+ var tagDidChange: ((String?) -> ())?
61+
62+ @objc private func textDidChange() {
63+ tagDidChange?(tagString)
64+ }
65+
60 var style: TagStyle = .normal {
66 var style: TagStyle = .normal {
61 didSet {
67 didSet {
62 updateStyle()
68 updateStyle()
@@ -70,6 +76,9 @@ class TagTableViewCell: UITableViewCell {
70
76
71 textField.borderStyle = .roundedRect
77 textField.borderStyle = .roundedRect
72 textField.adjustsFontSizeToFitWidth = false
78 textField.adjustsFontSizeToFitWidth = false
79+ textField.autocapitalizationType = .none
80+ textField.autocorrectionType = .no
81+ textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged)
73
82
74 contentView.addSubview(textField)
83 contentView.addSubview(textField)
75// textField.delegate = self
84// textField.delegate = self
@@ -92,7 +101,7 @@ class TagTableViewCell: UITableViewCell {
92 CGRect.infinite, limitedToNumberOfLines: 1)
101 CGRect.infinite, limitedToNumberOfLines: 1)
93 .height + 2*2
102 .height + 2*2
94
103
95− let frame = CGRect(x: 16, y: 8, width: bounds.width - 16*2, height: height)
104+ let frame = CGRect(x: 16, y: style == .large ? 32 : 4, width: bounds.width - 16*2, height: height)
96 textField.frame = frame
105 textField.frame = frame
97 print(textField.frame)
106 print(textField.frame)
98 }
107 }
@@ -103,8 +112,8 @@ class TagTableViewCell: UITableViewCell {
103
112
104
113
105 static let identifier = "io.Melk.tagTableViewCell"
114 static let identifier = "io.Melk.tagTableViewCell"
106− static let largeHeight: CGFloat = 37.5 + 8*2
115+ static let largeHeight: CGFloat = 37.5 + 16 + 32
107− static let normalHeight: CGFloat = 28.0 + 8*2
116+ static let normalHeight: CGFloat = 28.0 + 4*2
108}
117}
109
118
110//extension TagTableViewCell: UITextFieldDelegate {
119//extension TagTableViewCell: UITextFieldDelegate {
Melk/VKData.swift+34−34
@@ -7,10 +7,10 @@
7//
7//
8
8
9import Foundation
9import Foundation
10−//import SwiftyJSON
10+import SwiftyJSON
11import CoreLocation
11import CoreLocation
12import RealmSwift
12import RealmSwift
13−import SwiftyVK
13+//import SwiftyVK
14
14
15class VKData {
15class VKData {
16 // var users: [UserID: User] = [:]
16 // var users: [UserID: User] = [:]
@@ -31,7 +31,7 @@ class VKData {
31 // var polls: [PollID: Poll] = [:]
31 // var polls: [PollID: Poll] = [:]
32 // var photoAlbums: [PhotoAlbumID: PhotoAlbum] = [:]
32 // var photoAlbums: [PhotoAlbumID: PhotoAlbum] = [:]
33
33
34− class func processUser(json: SwiftyVK.JSON) -> User? {
34+ class func processUser(json: JSON) -> User? {
35 print("in")
35 print("in")
36 if let id = json["id"].int, let firstName = json["first_name"].string, let lastName = json["last_name"].string {
36 if let id = json["id"].int, let firstName = json["first_name"].string, let lastName = json["last_name"].string {
37 var user: User!
37 var user: User!
@@ -49,7 +49,7 @@ class VKData {
49 }
49 }
50 user.birthday = formatter.date(from: birthday)
50 user.birthday = formatter.date(from: birthday)
51 }
51 }
52− user.avatar = RealmPhoto.by(user.avatarID, surl: json["photo_max"].string)
52+ user.avatar = RealmPhoto.by(user.avatarID, surl: json["photo_100"].string)// was "photo_max"
53 user.photo = RealmPhoto.by(user.photoID, surl: json["photo_max_orig"].string)
53 user.photo = RealmPhoto.by(user.photoID, surl: json["photo_max_orig"].string)
54 if let friendStat = json["friend_status"].int {
54 if let friendStat = json["friend_status"].int {
55 user.friendStatus = FriendStatus(rawValue: friendStat)
55 user.friendStatus = FriendStatus(rawValue: friendStat)
@@ -90,7 +90,7 @@ class VKData {
90 return nil
90 return nil
91 }
91 }
92
92
93− class func processGeolocation(_ json: SwiftyVK.JSON) -> Geolocation? {
93+ class func processGeolocation(_ json: JSON) -> Geolocation? {
94 if let coords = json["coordinates"].string{
94 if let coords = json["coordinates"].string{
95 let nums = coords.characters.split(separator: " ").map({ (n) -> Double? in
95 let nums = coords.characters.split(separator: " ").map({ (n) -> Double? in
96 NumberFormatter().number(from: String(n))?.doubleValue
96 NumberFormatter().number(from: String(n))?.doubleValue
@@ -112,7 +112,7 @@ class VKData {
112 }
112 }
113 }
113 }
114
114
115− class func processMedia(_ json: SwiftyVK.JSON) -> Media? {
115+ class func processMedia(_ json: JSON) -> Media? {
116 if let type = json["type"].string {
116 if let type = json["type"].string {
117 switch type {
117 switch type {
118 case "photo":
118 case "photo":
@@ -174,7 +174,7 @@ class VKData {
174 return nil
174 return nil
175 }
175 }
176
176
177− class func processPhotoAlbum(_ json: SwiftyVK.JSON) -> PhotoAlbum? {
177+ class func processPhotoAlbum(_ json: JSON) -> PhotoAlbum? {
178 if let id = json["id"].int{
178 if let id = json["id"].int{
179 var album: PhotoAlbum!
179 var album: PhotoAlbum!
180 realmWrite { _ in
180 realmWrite { _ in
@@ -209,7 +209,7 @@ class VKData {
209 return nil
209 return nil
210 }
210 }
211
211
212− class func processGift(_ json: SwiftyVK.JSON) -> Gift? {
212+ class func processGift(_ json: JSON) -> Gift? {
213 if let id = json["id"].int {
213 if let id = json["id"].int {
214 var gift: Gift!
214 var gift: Gift!
215 realmWrite({ _ in
215 realmWrite({ _ in
@@ -221,7 +221,7 @@ class VKData {
221 return nil
221 return nil
222 }
222 }
223
223
224− class func processWallPostReply(_ json: SwiftyVK.JSON) -> WallPostReply? {
224+ class func processWallPostReply(_ json: JSON) -> WallPostReply? {
225 let likes = json["likes"]
225 let likes = json["likes"]
226 if let id = json["id"].int {
226 if let id = json["id"].int {
227 var reply: WallPostReply!
227 var reply: WallPostReply!
@@ -248,7 +248,7 @@ class VKData {
248 return nil
248 return nil
249 }
249 }
250
250
251− class func processPollAnswer(_ json: SwiftyVK.JSON) -> PollAnswer? {
251+ class func processPollAnswer(_ json: JSON) -> PollAnswer? {
252 if let id = json["id"].int,
252 if let id = json["id"].int,
253 let body = json["text"].string,
253 let body = json["text"].string,
254 let votes = json["votes"].int,
254 let votes = json["votes"].int,
@@ -258,7 +258,7 @@ class VKData {
258 return nil
258 return nil
259 }
259 }
260
260
261− class func processPoll(_ json: SwiftyVK.JSON) -> Poll? {
261+ class func processPoll(_ json: JSON) -> Poll? {
262 if let id = json["id"].int {
262 if let id = json["id"].int {
263 var poll: Poll!
263 var poll: Poll!
264 realmWrite { _ in
264 realmWrite { _ in
@@ -279,7 +279,7 @@ class VKData {
279 return nil
279 return nil
280 }
280 }
281
281
282− class func processPhoto(_ json: SwiftyVK.JSON) -> Photo? {
282+ class func processPhoto(_ json: JSON) -> Photo? {
283 if let id = json["id"].int {
283 if let id = json["id"].int {
284 var photo: Photo!
284 var photo: Photo!
285 realmWrite { _ in
285 realmWrite { _ in
@@ -289,13 +289,13 @@ class VKData {
289 photo.width.value = json["width"].int
289 photo.width.value = json["width"].int
290 photo.height.value = json["height"].int
290 photo.height.value = json["height"].int
291 var surl: String?
291 var surl: String?
292− if let imageSurl = json["photo_2560"].string {
292+// if let imageSurl = json["photo_2560"].string {
293− surl = imageSurl
293+// surl = imageSurl
294− } else if let imageSurl = json["photo_1280"].string {
294+// } else if let imageSurl = json["photo_1280"].string {
295− surl = imageSurl
295+// surl = imageSurl
296− } else if let imageSurl = json["photo_807"].string {
296+// } else if let imageSurl = json["photo_807"].string {
297− surl = imageSurl
297+// surl = imageSurl
298− } else if let imageSurl = json["photo_604"].string {
298+ /*} else*/ if let imageSurl = json["photo_604"].string {
299 surl = imageSurl
299 surl = imageSurl
300 } else if let imageSurl = json["photo_130"].string {
300 } else if let imageSurl = json["photo_130"].string {
301 surl = imageSurl
301 surl = imageSurl
@@ -312,7 +312,7 @@ class VKData {
312 }
312 }
313 }
313 }
314
314
315− class func processVideo(_ json: SwiftyVK.JSON) -> Video? {
315+ class func processVideo(_ json: JSON) -> Video? {
316 if let id = json["id"].int {
316 if let id = json["id"].int {
317 var video: Video!
317 var video: Video!
318 realmWrite { _ in
318 realmWrite { _ in
@@ -337,7 +337,7 @@ class VKData {
337 }
337 }
338 }
338 }
339
339
340− class func processAudio(_ json: SwiftyVK.JSON) -> Audio? {
340+ class func processAudio(_ json: JSON) -> Audio? {
341 if let id = json["id"].int {
341 if let id = json["id"].int {
342 var audio: Audio!
342 var audio: Audio!
343 realmWrite { _ in
343 realmWrite { _ in
@@ -354,7 +354,7 @@ class VKData {
354 }
354 }
355 }
355 }
356
356
357− class func processDocument(_ json: SwiftyVK.JSON) -> Document? {
357+ class func processDocument(_ json: JSON) -> Document? {
358 if let id = json["id"].int {
358 if let id = json["id"].int {
359 var doc: Document!
359 var doc: Document!
360 realmWrite { _ in
360 realmWrite { _ in
@@ -371,7 +371,7 @@ class VKData {
371 return nil
371 return nil
372 }
372 }
373
373
374− class func processSticker(_ json: SwiftyVK.JSON) -> Sticker? {
374+ class func processSticker(_ json: JSON) -> Sticker? {
375 if let id = json["id"].int,
375 if let id = json["id"].int,
376 let pack = json["product_id"].int {
376 let pack = json["product_id"].int {
377 var sticker: Sticker!
377 var sticker: Sticker!
@@ -387,7 +387,7 @@ class VKData {
387 return nil
387 return nil
388 }
388 }
389
389
390− class func processRating(_ json: SwiftyVK.JSON) -> Rating? {
390+ class func processRating(_ json: JSON) -> Rating? {
391 if let stars = json["stars"].int,
391 if let stars = json["stars"].int,
392 let reviewCount = json["reviews_count"].int {
392 let reviewCount = json["reviews_count"].int {
393 return Rating(stars: stars, reviewCount: reviewCount)
393 return Rating(stars: stars, reviewCount: reviewCount)
@@ -395,7 +395,7 @@ class VKData {
395 return nil
395 return nil
396 }
396 }
397
397
398− class func processButton(_ json: SwiftyVK.JSON) -> LinkButton? {
398+ class func processButton(_ json: JSON) -> LinkButton? {
399 if let title = json["title"].string,
399 if let title = json["title"].string,
400 let surl = json["url"].string,
400 let surl = json["url"].string,
401 let url = URL(string: surl) {
401 let url = URL(string: surl) {
@@ -404,7 +404,7 @@ class VKData {
404 return nil
404 return nil
405 }
405 }
406
406
407− class func processLink(_ json: SwiftyVK.JSON) -> Link? {
407+ class func processLink(_ json: JSON) -> Link? {
408 if let surl = json["url"].string {
408 if let surl = json["url"].string {
409 var link: Link!
409 var link: Link!
410 realmWrite({ (_) in
410 realmWrite({ (_) in
@@ -422,7 +422,7 @@ class VKData {
422 return nil
422 return nil
423 }
423 }
424
424
425− class func processWallPost(_ json: SwiftyVK.JSON) -> WallPost? {
425+ class func processWallPost(_ json: JSON) -> WallPost? {
426 let comments = json["comments"]
426 let comments = json["comments"]
427 let likes = json["likes"]
427 let likes = json["likes"]
428 let reposts = json["reposts"]
428 let reposts = json["reposts"]
@@ -430,7 +430,7 @@ class VKData {
430 var post: WallPost!
430 var post: WallPost!
431 realmWrite { _ in
431 realmWrite { _ in
432 post = WallPost.by(id: id)
432 post = WallPost.by(id: id)
433− post.owner = json["to_id"].int >>> {User.by(id: $0)}
433+ post.owner = json["owner_id"].int >>> {User.by(id: $0)}
434 post.creator = json["from_id"].int >>> {User.by(id: $0)}
434 post.creator = json["from_id"].int >>> {User.by(id: $0)}
435 post.date = json["date"].int >>> {Date.init(timeIntervalSince1970: TimeInterval($0))}
435 post.date = json["date"].int >>> {Date.init(timeIntervalSince1970: TimeInterval($0))}
436 post.body = json["text"].string
436 post.body = json["text"].string
@@ -469,7 +469,7 @@ class VKData {
469 return nil
469 return nil
470 }
470 }
471
471
472− class func prpocessServiceMessageAction(_ json: SwiftyVK.JSON) -> ServiceMessageAction? {
472+ class func prpocessServiceMessageAction(_ json: JSON) -> ServiceMessageAction? {
473 if let sAction = json["action"].string {
473 if let sAction = json["action"].string {
474 switch sAction {
474 switch sAction {
475 case "chat_photo_update":
475 case "chat_photo_update":
@@ -519,7 +519,7 @@ class VKData {
519 return nil
519 return nil
520 }
520 }
521
521
522− class func processMessage(_ json: SwiftyVK.JSON) -> Message? {
522+ class func processMessage(_ json: JSON) -> Message? {
523 if let user = json["user_id"].int >>> {User.by(id: $0)},
523 if let user = json["user_id"].int >>> {User.by(id: $0)},
524 let date = json["date"].int >>> {Date(timeIntervalSince1970: TimeInterval($0))},
524 let date = json["date"].int >>> {Date(timeIntervalSince1970: TimeInterval($0))},
525 let id = json["id"].int {
525 let id = json["id"].int {
@@ -556,7 +556,7 @@ class VKData {
556 }
556 }
557 }
557 }
558
558
559− class func processGenericMessage(_ json: SwiftyVK.JSON) -> GenericMessage? {
559+ class func processGenericMessage(_ json: JSON) -> GenericMessage? {
560 if let user = json["user_id"].int >>> {User.by(id: $0)},
560 if let user = json["user_id"].int >>> {User.by(id: $0)},
561 let date = json["date"].int >>> {Date(timeIntervalSince1970: TimeInterval($0))} {
561 let date = json["date"].int >>> {Date(timeIntervalSince1970: TimeInterval($0))} {
562 let message = GenericMessage()
562 let message = GenericMessage()
@@ -587,7 +587,7 @@ class VKData {
587
587
588
588
589
589
590− class func processNotificationSettings(_ json: SwiftyVK.JSON) -> ChatNotificationSettings? {
590+ class func processNotificationSettings(_ json: JSON) -> ChatNotificationSettings? {
591 if let sound = json["sound"].int,
591 if let sound = json["sound"].int,
592 let date = json["disabled_until"].int {
592 let date = json["disabled_until"].int {
593 if date == -1 {
593 if date == -1 {
@@ -600,7 +600,7 @@ class VKData {
600 }
600 }
601 }
601 }
602
602
603− class func processMultichat(_ json: SwiftyVK.JSON) -> Multichat? { // from .getChat
603+ class func processMultichat(_ json: JSON) -> Multichat? { // from .getChat
604 if let id = json["id"].int {
604 if let id = json["id"].int {
605 var chat: Multichat!
605 var chat: Multichat!
606 realmWrite { _ in
606 realmWrite { _ in
@@ -634,7 +634,7 @@ class VKData {
634 return nil
634 return nil
635 }
635 }
636
636
637− class func processDialog(_ json: SwiftyVK.JSON) -> Dialog? {
637+ class func processDialog(_ json: JSON) -> Dialog? {
638 let message = json["message"]
638 let message = json["message"]
639 if let id = message["chat_id"].int { // Multichat
639 if let id = message["chat_id"].int { // Multichat
640 if let sMessage = processMessage(message) {
640 if let sMessage = processMessage(message) {
Melk/VKPublicCrap.swift+24−21
@@ -6,35 +6,38 @@
6// Copyright © 2016 Ilya Kos. All rights reserved.
6// Copyright © 2016 Ilya Kos. All rights reserved.
7//
7//
8
8
9−import SwiftyVK
9+//import SwiftyVK
10−
10+/*
11let permissions: Set<VK.Scope> = [
11let permissions: Set<VK.Scope> = [
12− .notify,
12+// .notify,
13− .friends,
13+// .friends,
14− .photos,
14+// .photos,
15− .audio,
15+// .audio,
16− .video,
16+// .video,
17− .docs,
17+// .docs,
18− .notes,
18+// .notes,
19 .pages,
19 .pages,
20− .status,
20+// .status,
21− .offers,
21+// .offers,
22− .questions,
22+// .questions,
23 .wall,
23 .wall,
24 .groups,
24 .groups,
25− .messages,
25+// .messages,
26− .email,
26+// .email,
27− .notifications,
27+// .notifications,
28− .stats,
28+// .stats,
29− .ads,
29+// .ads,
30− .offline,
30+// .offline,
31− .market
31+// .market,
32]
32]
33+ */
33−let vkAPIVersion = "5.60"
34+let vkAPIVersion = "5.67"
34−let appID = "5529752"
35+let appID = "6097091"
35
36
36let multichatchatId = 2000000000
37let multichatchatId = 2000000000
37
38
38enum Methods {
39enum Methods {
39 static let getDialogs = "getDialogs"
40 static let getDialogs = "getDialogs"
40}
41}
42+
43+let accessKey = "363b0ecf363b0ecf363b0ecf9a3666060c3363b363b0ecf6f6cb5bcd453ddb4d0fadf8a"
Melk/VKServer.swiftnew file+123
@@ -0,0 +1,123 @@
1+//
2+// VKServer.swift
3+// Melk
4+//
5+// Created by Ilya Kos on 7/12/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+import Alamofire
11+
12+class VKServer {
13+ static func get(photo: Photo, completion: @escaping ((Data) -> ())) {
14+ func string(`for` photo: Photo) -> String? {
15+ if let ownerID = photo.owner?.id {
16+ let id = photo.id
17+ var out = "\(ownerID)_\(id)"
18+ if let accessKey = photo.accessKey {
19+ out.append("_\(accessKey)")
20+ }
21+ return out
22+ }
23+ return nil
24+ }
25+ let params: Parameters = [
26+ "photos": string(for: photo)!
27+ ]
28+ Alamofire.request(url(for: "photos.getById"), parameters: params)
29+ .validate().responseData { (response) in
30+ switch response.result {
31+ case .success(let data):
32+ completion(data)
33+ case .failure(let error):
34+ print(error)
35+ }
36+ }
37+ }
38+ static func get(video: Video, completion: @escaping ((Data) -> ())) {
39+ func string(`for` video: Video) -> String? {
40+ if let ownerID = video.owner?.id {
41+ let id = video.id
42+ var out = "\(ownerID)_\(id)"
43+ if let accessKey = video.accessKey {
44+ out.append("_\(accessKey)")
45+ }
46+ return out
47+ }
48+ return nil
49+ }
50+ let params: Parameters = [
51+ "videos": string(for: video)!
52+ ]
53+ Alamofire.request(url(for: "videos.get"), parameters: params)
54+ .validate().responseData { (response) in
55+ switch response.result {
56+ case .success(let data):
57+ completion(data)
58+ case .failure(let error):
59+ print(error)
60+ }
61+ }
62+ }
63+ static func get(doc: Document, completion: @escaping ((Data) -> ())) {
64+ if let userID = doc.owner?.id {
65+ let id = doc.id
66+ let params: Parameters = [
67+ "docs": "\(userID)_\(id)"
68+ ]
69+ Alamofire.request(url(for: "docs.getById"), parameters: params)
70+ .validate().responseData(completionHandler: { (response) in
71+ switch response.result {
72+ case .success(let data):
73+ completion(data)
74+ case .failure(let error):
75+ print(error)
76+ }
77+ })
78+ }
79+ }
80+ static func get(user: User, completion: @escaping ((Data) -> ())) {
81+ let id = user.id
82+ let params: Parameters = [
83+ "user_ids": id,
84+ "fields": "photo_100"
85+ ]
86+ Alamofire.request(url(for: "users.get"), parameters: params)
87+ .validate().responseData { (response) in
88+ switch response.result {
89+ case .success(let data):
90+ completion(data)
91+ case .failure(let error):
92+ print(error)
93+ }
94+ }
95+ }
96+ static func get(wallPost: WallPost, completion: @escaping ((Data) -> ())) {
97+ if let userID = wallPost.owner?.id {
98+ let id = wallPost.id
99+ let params: Parameters = [
100+ "posts": "\(userID)_\(id)"
101+ ]
102+ Alamofire.request(url(for: "wall.getById"), parameters: params)
103+ .validate().responseData(completionHandler: { (response) in
104+ switch response.result {
105+ case .success(let data):
106+ completion(data)
107+ case .failure(let error):
108+ print(error)
109+ }
110+ })
111+ }
112+ }
113+
114+ static func get(wallPostReply: WallPostReply, completion: @escaping ((Data) -> ())) {
115+ // TODO: Get wallPostReply
116+ }
117+
118+}
119+
120+fileprivate func url(`for` method: String) -> URL {
121+ let surl = "https://api.vk.com/method/\(method)?access_token=\(accessKey)&v=\(vkAPIVersion)&lang=0"
122+ return URL.init(string: surl)!
123+}
Melk/VkServices.swiftdeleted−207
@@ -1,207 +0,0 @@
1−//
2−// VkServices.swift
3−// golos
4−//
5−// Created by Ilya Kos on 7/24/16.
6−// Copyright © 2016 Ilya Kos. All rights reserved.
7−//
8−
9−import UIKit
10−import Alamofire
11−import SwiftyVK
12−
13−// let realm = try! Realm()
14−
15−class VkServices {
16− class func getDialogs(offset: Int = 0, count: Int = 200, completion: (() -> ())? = nil) { // Messages
17− VK.API.remote(method: Methods.getDialogs, parameters:
18− [VK.Arg.offset: String(offset),
19− VK.Arg.count: String(count)])
20− .send(onSuccess: { (response) in
21− if let usersJSON = response[1].array,
22− let chatUsersJSON = response[2].array,
23− let dialogs = response[0]["items"].array,
24− let chatUserInvitersJSON = response[3].array,
25− let multichatsJSON = response[4].array { // TODO: Catch error from vk
26− userQueue.async {
27− let _ = usersJSON.map(VKData.processUser)
28− let _ = chatUsersJSON.map(VKData.processUser)
29− let _ = chatUserInvitersJSON.flatMap(VKData.processUser)
30− let _ = multichatsJSON.map(VKData.processMultichat)
31− let _ = dialogs.map(VKData.processDialog)
32− print("DONEZO")
33− DispatchQueue.main.async {
34− completion?()
35− }
36− }
37− }
38− }, onError: { error in
39− print(error)
40− }) // TODO: Handle error
41− }
42−
43−
44− /// - Parameters:
45− /// - dialog: in the `200000000` and `-` fomat
46− /// - count: count
47− /// - offset: offset
48− /// - completion: completion handler
49− class func getMessages(forDialog dialog: DialogID, count: Int = 20, offset: Int = 0, completion: (() -> ())? = nil) {
50− var params: [VK.Arg: String] = [
51− VK.Arg.peerId: String(dialog),
52− VK.Arg.offset: String(offset),
53− VK.Arg.count: String(count)]
54− var request = VK.API.Messages.getHistory()
55− request.add(parameters: params)
56− request.send(onSuccess: { (response) in
57− userQueue.async {
58− realmWrite { _ in
59− let rDialog = RealmDialog.by(peerID: dialog)
60− var f = rDialog.value
61− f <- response["items"].array?.flatMap(VKData.processMessage)
62− f?.lastReadMessageID = max(response["out_read"].intValue, response["in_read"].intValue) // TODO: Probrably should use seperate things for readin and readout
63− response["count"].int >>> { rDialog.value?.count.value = $0 }
64− }
65− }
66− }, onError: nil) // TODO: Handle error
67−
68− completion?()
69− }
70−
71− class func send(_ message: Message, to dialog: RealmDialog, completion: (() -> ())? = nil) {
72− let date = Date()
73− let time = Int(date.timeIntervalSince1970) * 100
74− var params: [VK.Arg: String] = [:]
75− if let d = dialog.value {
76− switch d {
77− case let chat as Chat:
78− params[VK.Arg.peerId] = String(chat.userID)
79− case let mchat as Multichat:
80− params[VK.Arg.chatId] = String(mchat.id)
81− default:
82− break
83− }
84− }
85− if dialog.dateLastSent == date {
86− realmWrite({ (_) in
87− dialog.dateSentNumber += 1
88− })
89− } else {
90− realmWrite({ (_) in
91− dialog.dateSentNumber = 1
92− dialog.dateLastSent = date
93− })
94− }
95−
96− var i = 0
97− func photoUploadRequest() {
98− if i < message.outPhotos.count {
99− if let data = UIImagePNGRepresentation(message.outPhotos[i]) {
100− let request = VK.API.Upload.Photo.toMessage(SwiftyVK.Media.init(imageData: data, type: .PNG))
101− request.send(onSuccess: { (response) in
102− if let photo = VKData.processPhoto(response) { // TODO: this probrably shouldnt happen on the main queue
103− message.attachments.append(RealmMedia(value: Media.photo(photo)))
104− }
105− i += 1
106− photoUploadRequest()
107− }, onError: nil) // TODO: Handle error
108− } else {
109− i += 1
110− photoUploadRequest()
111− }
112− }
113− }
114−
115−
116−
117− params[VK.Arg.randomId] = String(time + dialog.dateSentNumber)
118− params[VK.Arg.message] = String(message.body)
119− message.geolocation?.geoCoordinates?.coordinate.latitude >>> {params[VK.Arg.lat] = String($0)}
120− message.geolocation?.geoCoordinates?.coordinate.longitude >>> {params[VK.Arg.long] = String($0)}
121− var att = ""
122− var stickerID: Int?
123− for m in message.attachments {
124− m.string >>> { att.append("\($0),") }
125− if stickerID == nil {
126− if let v = m.value {
127− switch v {
128− case .sticker(let s):
129− stickerID = s.id
130− default:
131− break
132− }
133− }
134− }
135− }
136− params[VK.Arg.attachments] = att
137− var fm = ""
138− for m in message.forwardedMessages {
139− m.id >>> {fm.append("\($0),")}
140− }
141− params[VK.Arg.forwardMessages] = fm
142− stickerID >>> {params[VK.Arg.stickerId] = String($0)}
143− VK.API.Messages.send(params).send(onSuccess: { (response) in
144− if let id = response.int {
145− realmWrite({ (_) in
146− message.id = id
147− dialog.value?.messages <~ message // TODO: Uncomment this
148− })
149− }
150− completion?()
151− }, onError: nil) // TODO: Handle error
152− }
153− class func getMultichat(for id: MultichatID) {
154− let params: [VK.Arg: String] = [
155− VK.Arg.chatId: String(id),
156− VK.Arg.fields: "nickname, screen_name, sex, bdate, city, country, timezone, photo_50, photo_100, photo_200_orig, has_mobile, contacts, education, online, counters, relation, last_seen, status, can_write_private_message, can_see_all_posts, can_post, universities",
157−
158− ]
159− VK.API.Messages.getChat(params).send(onSuccess: { (response) in
160− VKData.processMultichat(response)
161− }, onError: nil) // TODO: Handle error
162− }
163−
164−}
165−
166−fileprivate extension RealmMedia {
167− var string: String? {
168− if let v = value {
169− switch v {
170− case .photo(let p):
171− if let o = p.owner {
172− var out = "photo\(o.id)_\(p.id)"
173− p.accessKey >>> {out.append("_\($0)")}
174− return out
175− }
176− case .video(let p):
177− if let o = p.owner {
178− var out = "video\(o.id)_\(p.id)"
179− p.accessKey >>> {out.append("_\($0)")}
180− return out
181− }
182− case .audio(let p):
183− if let o = p.owner {
184− return "audio\(o.id)_\(p.id)"
185− }
186− case .doc(let p), .graffiti(let p):
187− if let o = p.owner {
188− return "doc\(o.id)_\(p.id)"
189− }
190− case .wall(let p):
191− if let o = p.owner {
192− return "photo\(o.id)_\(p.id)"
193− }
194− case .market(let p):
195− if let o = p.owner {
196− return "photo\(o.id)_\(p.id)"
197− }
198− default: break
199− }
200− }
201− return nil
202− }
203−}
204−
205−private func URLFor(method: String) -> String {
206− return "https://api.vk.com/method/" + method
207−}
README.mdnew file+17
@@ -0,0 +1,17 @@
1+# Melk
2+
3+_Might not work properly with new streaming API. This project was developed while the API was in beta._
4+
5+[![Melk demo](http://img.youtube.com/vi/9SNc0bLDCL0/0.jpg)](http://www.youtube.com/watch?v=9SNc0bLDCL0)
6+
7+При проведении различных мероприятий организаторы довольно часто призывают людей, посещающих эти мероприятия, выложить у себя в соц сети что-нибудь об этом мероприятии, чаще всего под мероприятие даже выделяется отдельный хештег.
8+
9+Для привлечения внимания к хештегу и поощрения его использования можно выводить некоторые из выложенных с хештегом записей туда, где все смогут это увидеть.
10+
11+При проведении больших мероприятий желательно использовать большое количество независимых систем, чтобы если летит что-то одно, все остальное продолжало работать.
12+
13+Полноценный десктопный компьютер для данной цели не нужен, поэтому приложение работает просто на телефоне или планшете.
14+
15+Самый первый хештег или ключевое слово будет крупно показано вместе со всеми записями.
16+
17+После того, как будет нажата кнопка “Present”, записи с основным хештегом будут выводиться на любые подключенные к устройству экраны.