diff --git a/Podfile b/Podfile index 71e30f4c9..60e5876e4 100644 --- a/Podfile +++ b/Podfile @@ -3,7 +3,7 @@ platform :ios, '8.0' use_frameworks! def pods - pod 'Kanna' + pod 'Kanna', '1.0.2' pod 'Navi' pod 'Appsee' pod 'Alamofire' @@ -27,3 +27,5 @@ end target 'Yep' do pods end + + diff --git a/Podfile.lock b/Podfile.lock index f7e0e9741..8bb68fc2f 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - 1PasswordExtension (1.8) + - 1PasswordExtension (1.8.1) - Alamofire (3.3.1) - Appsee (2.2) - Base64 (1.1.2) @@ -7,18 +7,18 @@ PODS: - Fabric (1.6.7) - FXBlurView (1.6.4) - JPush-iOS-SDK (1.8.8) - - Kanna (1.0.6) + - Kanna (1.0.2) - KeyboardMan (0.6.0) - - Kingfisher (2.2.2) + - Kingfisher (2.3.1) - MonkeyKing (0.0.2) - Navi (0.4.2) - pop (1.0.9) - Proposer (0.8.0) - - Realm (0.98.8): - - Realm/Headers (= 0.98.8) - - Realm/Headers (0.98.8) - - RealmSwift (0.98.8): - - Realm (= 0.98.8) + - Realm (0.99.1): + - Realm/Headers (= 0.99.1) + - Realm/Headers (0.99.1) + - RealmSwift (0.99.1): + - Realm (= 0.99.1) - Ruler (0.7.0) - SocketRocket (0.5.0) - TPKeyboardAvoiding (1.3) @@ -32,7 +32,7 @@ DEPENDENCIES: - Fabric - FXBlurView - JPush-iOS-SDK (= 1.8.8) - - Kanna + - Kanna (= 1.0.2) - KeyboardMan - Kingfisher - MonkeyKing (= 0.0.2) @@ -45,7 +45,7 @@ DEPENDENCIES: - TPKeyboardAvoiding SPEC CHECKSUMS: - 1PasswordExtension: 627d5aecf210c7636968faf5a4c3546c9531385b + 1PasswordExtension: 00d6f4caae77c19ba33045b8569fbeb9689f0d1d Alamofire: 369bc67b6f5ac33ded3648d7bd21c5bfb91c2ecc Appsee: c95173e65acaab0ffc8a082c70633c9f5f45d5f0 Base64: cecfb41a004124895a7bcee567a89bae5a89d49b @@ -53,15 +53,15 @@ SPEC CHECKSUMS: Fabric: caf7580c725e64db144f610ac65cd60956911dc7 FXBlurView: db786c2561cb49a09ae98407f52460096ab8a44f JPush-iOS-SDK: d4c097d8abbdd0837e24d44b41aacf64a6feddfd - Kanna: 9f30945bbd293a609f698ea164687ccc0990ff60 + Kanna: 059afb193a9f8ea34f46cb55a8b11bf4257efad8 KeyboardMan: 665211f180093f5bb3c6ff7e8b7f858c432c6ccf - Kingfisher: 7e8de5b0cf7b5dc8ec559d193029a0859df93ef1 + Kingfisher: 6d3fbada4829cbc7b89e883e8218c28afeb64e42 MonkeyKing: 9be24307843a80f4cd3eca66adc12c67aefb83bb Navi: f743d94c9879e646ee399b404d0b53100c66a8bd pop: f667631a5108a2e60d9e8797c9b32ddaf2080bce Proposer: 17cd7fd2509b0d8d7c9e33815d648087c7dcaa81 - Realm: 0e293bb62999730599efc3048896bbd4f2e43bcd - RealmSwift: 064262d38113f23ff3508fb20a0a922e696bec01 + Realm: 522a0cc8e1f8755ee56d7dc16ca0fc380d045766 + RealmSwift: 1d9c4299ef5dbf575c5da09fc706ed726327cfa0 Ruler: 51bd16cc00638702ef71713f80073588a6c7e682 SocketRocket: 2c51efccd2d73c99a923407ca4b06e7e9da95dbf TPKeyboardAvoiding: a5138f318c06fb3e151f886e18ce4a72695d9cbe diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index 399ee0ee5..2fe058988 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ 0A8F54B61AB67D11004AD60E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0A8F54B41AB67D11004AD60E /* Main.storyboard */; }; 0A8F54B81AB67D11004AD60E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0A8F54B71AB67D11004AD60E /* Images.xcassets */; }; 0A8F54BB1AB67D11004AD60E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A8F54B91AB67D11004AD60E /* LaunchScreen.xib */; }; - 0A8F54C71AB67D11004AD60E /* YepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8F54C61AB67D11004AD60E /* YepTests.swift */; }; 0A90187E1B01152800AE4B7F /* ProfileSocialAccountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A90187C1B01152800AE4B7F /* ProfileSocialAccountCell.swift */; }; 0A90187F1B01152800AE4B7F /* ProfileSocialAccountCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A90187D1B01152800AE4B7F /* ProfileSocialAccountCell.xib */; }; 0A9018821B0120A500AE4B7F /* OAuthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9018811B0120A500AE4B7F /* OAuthViewController.swift */; }; @@ -40,8 +39,9 @@ 0AD7EA6C1AC3ED4300617758 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA6B1AC3ED4300617758 /* Security.framework */; }; 0AEB1CC61B0E742400178C9C /* Double+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEB1CC51B0E742400178C9C /* Double+Yep.swift */; }; 27CDF0ACB80E10E6AC5596F6 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34A6628BE8C7FFA96376C5C6 /* CoreText.framework */; }; - 33180F581CC8887E00CACF38 /* LeftShareFeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33180F571CC8887E00CACF38 /* LeftShareFeedCell.swift */; }; - 33180F5A1CC8889500CACF38 /* RightShareFeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33180F591CC8889500CACF38 /* RightShareFeedCell.swift */; }; + 33180F5C1CC9C58100CACF38 /* DataSample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33180F5B1CC9C58100CACF38 /* DataSample.swift */; }; + 336563F81CCF2FF800598378 /* LeftShareFeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 336563F71CCF2FF800598378 /* LeftShareFeedCell.swift */; }; + 336563FA1CCF300200598378 /* RightShareFeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 336563F91CCF300200598378 /* RightShareFeedCell.swift */; }; 33893E3A1CB7C62E00B276C1 /* NewFeed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 33893E371CB7C62E00B276C1 /* NewFeed.storyboard */; }; 33893E3B1CB7C62E00B276C1 /* NewFeedPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33893E381CB7C62E00B276C1 /* NewFeedPreviewViewController.swift */; }; 33893E3C1CB7C62E00B276C1 /* NewFeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33893E391CB7C62E00B276C1 /* NewFeedViewController.swift */; }; @@ -55,7 +55,6 @@ 50008D2D1CAB76AD00245296 /* SearchableItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50008D2C1CAB76AD00245296 /* SearchableItemType.swift */; }; 50008D321CAE0D9A00245296 /* SearchConversationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50008D311CAE0D9A00245296 /* SearchConversationsViewController.swift */; }; 50008D341CAE0DC700245296 /* SearchConversations.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50008D331CAE0DC700245296 /* SearchConversations.storyboard */; }; - 50008D361CAE114300245296 /* ConversationsSearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50008D351CAE114300245296 /* ConversationsSearchTransition.swift */; }; 50008D3A1CAE537400245296 /* SearchedFeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50008D381CAE537400245296 /* SearchedFeedCell.swift */; }; 50008D3B1CAE537400245296 /* SearchedFeedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50008D391CAE537400245296 /* SearchedFeedCell.xib */; }; 5008A1931B8DB8B500E94274 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5008A1951B8DB8B500E94274 /* Localizable.strings */; }; @@ -85,6 +84,8 @@ 501690181BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501690161BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift */; }; 501690191BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 501690171BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib */; }; 5016901C1BFB22180096C4F9 /* SocialWorkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5016901B1BFB22180096C4F9 /* SocialWorkService.swift */; }; + 5017FC1D1CD095E100DFCB71 /* YepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5017FC1C1CD095E100DFCB71 /* YepTests.swift */; }; + 5017FC251CD0960200DFCB71 /* SyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5017FC241CD0960200DFCB71 /* SyncTests.swift */; }; 501813261BA07CED0025873C /* Activities.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 501813251BA07CED0025873C /* Activities.xcassets */; }; 5018132C1BA3CFEF0025873C /* WeChatActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018132B1BA3CFEF0025873C /* WeChatActivity.swift */; }; 502048241B0F1BB3002EBFC7 /* SearchedUsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502048231B0F1BB3002EBFC7 /* SearchedUsersViewController.swift */; }; @@ -158,6 +159,10 @@ 505082331C21130100643CEB /* FeedGithubRepoContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505082321C21130100643CEB /* FeedGithubRepoContainerView.swift */; }; 505082351C212E9200643CEB /* FeedLocationContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505082341C212E9200643CEB /* FeedLocationContainerView.swift */; }; 505082371C21377E00643CEB /* FeedVoiceContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505082361C21377E00643CEB /* FeedVoiceContainerView.swift */; }; + 505243A81CD983160094B471 /* ChangeMobileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505243A71CD983160094B471 /* ChangeMobileViewController.swift */; }; + 505243AA1CD983390094B471 /* ChangeMobile.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 505243A91CD983390094B471 /* ChangeMobile.storyboard */; }; + 505243AD1CD9905C0094B471 /* VerifyChangedMobile.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 505243AC1CD9905C0094B471 /* VerifyChangedMobile.storyboard */; }; + 505243AF1CD990750094B471 /* VerifyChangedMobileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505243AE1CD990750094B471 /* VerifyChangedMobileViewController.swift */; }; 5052620B1C51CDE600292C6A /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AC40E321C47A8FE0024FC63 /* libz.tbd */; }; 505262101C52050700292C6A /* ConcurrentOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5052620F1C52050700292C6A /* ConcurrentOperation.swift */; }; 505262121C5205AB00292C6A /* UploadAttachmentOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505262111C5205AB00292C6A /* UploadAttachmentOperation.swift */; }; @@ -202,8 +207,16 @@ 506EC20A1AC95BFC00CE42B3 /* UIFont+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */; }; 507682F01C9FE8F7006A973E /* SearchContactsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507682EF1C9FE8F7006A973E /* SearchContactsViewController.swift */; }; 507682F31C9FE921006A973E /* SearchContacts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 507682F21C9FE921006A973E /* SearchContacts.storyboard */; }; - 507682F51C9FED1F006A973E /* ContactsSearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507682F41C9FED1F006A973E /* ContactsSearchTransition.swift */; }; 5076C56A1AC46B9F00B22952 /* FayeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5076C5691AC46B9F00B22952 /* FayeService.swift */; }; + 507813101CD09FEE00F193AB /* ClearTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5078130F1CD09FEE00F193AB /* ClearTests.swift */; }; + 507813121CD0A45800F193AB /* RealmTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507813111CD0A45800F193AB /* RealmTests.swift */; }; + 507813141CD0B0CF00F193AB /* ServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507813131CD0B0CF00F193AB /* ServiceTests.swift */; }; + 507813191CD1B01A00F193AB /* ShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507813181CD1B01A00F193AB /* ShortcutItems.swift */; }; + 5078131B1CD1B04400F193AB /* ShortcutType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5078131A1CD1B04400F193AB /* ShortcutType.swift */; }; + 5078131D1CD1E33A00F193AB /* OpenGraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5078131C1CD1E33A00F193AB /* OpenGraphTests.swift */; }; + 5078131F1CD1EB5000F193AB /* NSURL+YepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5078131E1CD1EB5000F193AB /* NSURL+YepTests.swift */; }; + 507813221CD2F9C100F193AB /* DeviceGuru+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507813211CD2F9C100F193AB /* DeviceGuru+Yep.swift */; }; + 507813241CD2FEF400F193AB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 507813231CD2FEF400F193AB /* Images.xcassets */; }; 507CF1681AFC84A500E261B4 /* CustomNavigationBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CF1671AFC84A500E261B4 /* CustomNavigationBarViewController.swift */; }; 507F65F71B2EBC2700A35FDC /* MediaPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507F65F61B2EBC2700A35FDC /* MediaPreviewView.swift */; }; 5080EB1C1B93F43A006F3C2D /* PodsHelpYepViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5080EB1B1B93F43A006F3C2D /* PodsHelpYepViewController.swift */; }; @@ -249,6 +262,7 @@ 5099B9C71AB99F7B002F940B /* RegisterPickAvatarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099B9C61AB99F7B002F940B /* RegisterPickAvatarViewController.swift */; }; 5099B9C91AB9A7CB002F940B /* CameraPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099B9C81AB9A7CB002F940B /* CameraPreviewView.swift */; }; 509B120F1C436E7C0000BE71 /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509B120E1C436E7C0000BE71 /* MentionView.swift */; }; + 509F12951CCF14130073CB42 /* XCUIElement+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509F12941CCF14130073CB42 /* XCUIElement+Yep.swift */; }; 50A0E3011C3E18A10025BEFC /* CLLocationCoordinate2D+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A0E3001C3E18A10025BEFC /* CLLocationCoordinate2D+Yep.swift */; }; 50A1FA761C0D47D300EEDA74 /* SubscribeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A1FA751C0D47D300EEDA74 /* SubscribeView.swift */; }; 50A640E01B6F38CF0050E4A3 /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A640DF1B6F38CF0050E4A3 /* NotificationsViewController.swift */; }; @@ -278,6 +292,9 @@ 50B38FCC1B6B37F00064C132 /* FeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FCB1B6B37F00064C132 /* FeedbackViewController.swift */; }; 50B3C9501CC8649500623E29 /* SearchedFeedAnyImagesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B3C94F1CC8649500623E29 /* SearchedFeedAnyImagesCell.swift */; }; 50B3C9521CC873F000623E29 /* SearchFeedsFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B3C9511CC873F000623E29 /* SearchFeedsFooterView.swift */; }; + 50B3C9561CC9B66A00623E29 /* SearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B3C9551CC9B66A00623E29 /* SearchTransition.swift */; }; + 50B3C9581CC9BA9000623E29 /* SearchTriggerRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B3C9571CC9BA9000623E29 /* SearchTriggerRepresentation.swift */; }; + 50B3C95A1CC9BB2F00623E29 /* SearchActionRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B3C9591CC9BB2F00623E29 /* SearchActionRepresentation.swift */; }; 50B6A03B1CA27A6000C460E3 /* TableSectionTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B6A03A1CA27A6000C460E3 /* TableSectionTitleView.swift */; }; 50B6A03F1CA282B100C460E3 /* ActionSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B6A03E1CA282B100C460E3 /* ActionSheetView.swift */; }; 50B6A0421CA395A200C460E3 /* SearchedContactsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B6A0401CA395A200C460E3 /* SearchedContactsCell.swift */; }; @@ -307,6 +324,7 @@ 50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */; }; 50D45FC31C44D2300044C95A /* OpenGraphService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D45FC21C44D2300044C95A /* OpenGraphService.swift */; }; 50D4D0DB1AFA825B005AEB6F /* MediaControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */; }; + 50D50AC31CCE01D300FB5753 /* YepUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D50AC21CCE01D300FB5753 /* YepUITests.swift */; }; 50DBDEF11C48C2240044E21B /* OpenGraphInfoType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDEF01C48C2240044E21B /* OpenGraphInfoType.swift */; }; 50DBDEFD1C4CCB030044E21B /* ChatLeftTextURLCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDEFB1C4CCB030044E21B /* ChatLeftTextURLCell.swift */; }; 50DBDF011C4CE75E0044E21B /* ChatRightTextURLCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDEFF1C4CE75E0044E21B /* ChatRightTextURLCell.swift */; }; @@ -358,7 +376,6 @@ 50E34C221CB75774007CAE80 /* SearchedDiscoveredUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50E34C201CB75774007CAE80 /* SearchedDiscoveredUserCell.xib */; }; 50E34C261CBB8A37007CAE80 /* SearchFeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E34C251CBB8A37007CAE80 /* SearchFeedsViewController.swift */; }; 50E34C281CBB8A49007CAE80 /* SearchFeeds.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50E34C271CBB8A49007CAE80 /* SearchFeeds.storyboard */; }; - 50E34C2A1CBB8CAA007CAE80 /* FeedsSearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E34C291CBB8CAA007CAE80 /* FeedsSearchTransition.swift */; }; 50E34C2C1CBDEC31007CAE80 /* FeedsMoreViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E34C2B1CBDEC31007CAE80 /* FeedsMoreViewManager.swift */; }; 50E34C2F1CBE2A33007CAE80 /* CreatorsOfBlockedFeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E34C2E1CBE2A33007CAE80 /* CreatorsOfBlockedFeedsViewController.swift */; }; 50E34C311CBE2A48007CAE80 /* CreatorsOfBlockedFeeds.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50E34C301CBE2A48007CAE80 /* CreatorsOfBlockedFeeds.storyboard */; }; @@ -422,7 +439,14 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 0A8F54C11AB67D11004AD60E /* PBXContainerItemProxy */ = { + 5017FC1F1CD095E100DFCB71 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0A8F54A31AB67D11004AD60E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0A8F54AA1AB67D11004AD60E; + remoteInfo = Yep; + }; + 50D50AC51CCE01D300FB5753 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0A8F54A31AB67D11004AD60E /* Project object */; proxyType = 1; @@ -442,15 +466,13 @@ 0A3E60661ACCAECE003DC853 /* YepLocationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepLocationService.swift; path = Services/YepLocationService.swift; sourceTree = ""; }; 0A5D83931AC5D3C8000045BF /* YepTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepTabBarController.swift; path = ViewControllers/TabBar/YepTabBarController.swift; sourceTree = ""; }; 0A7A7D551AF5E00600A7F5F5 /* YepHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepHelper.swift; path = Configs/YepHelper.swift; sourceTree = ""; }; + 0A884441143C544F0CD78CF8 /* Pods_YepTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YepTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0A8F54AB1AB67D11004AD60E /* Yep.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Yep.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0A8F54AF1AB67D11004AD60E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0A8F54B01AB67D11004AD60E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0A8F54B51AB67D11004AD60E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 0A8F54B71AB67D11004AD60E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 0A8F54BA1AB67D11004AD60E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 0A8F54C01AB67D11004AD60E /* YepTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YepTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 0A8F54C51AB67D11004AD60E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 0A8F54C61AB67D11004AD60E /* YepTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YepTests.swift; sourceTree = ""; }; 0A90187C1B01152800AE4B7F /* ProfileSocialAccountCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileSocialAccountCell.swift; path = Views/Cells/ProfileSocialAccount/ProfileSocialAccountCell.swift; sourceTree = ""; }; 0A90187D1B01152800AE4B7F /* ProfileSocialAccountCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileSocialAccountCell.xib; path = Views/Cells/ProfileSocialAccount/ProfileSocialAccountCell.xib; sourceTree = ""; }; 0A9018811B0120A500AE4B7F /* OAuthViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OAuthViewController.swift; path = ViewControllers/OAuth/OAuthViewController.swift; sourceTree = ""; }; @@ -469,8 +491,10 @@ 0AD7EA691AC3ED3D00617758 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 0AD7EA6B1AC3ED4300617758 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 0AEB1CC51B0E742400178C9C /* Double+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Double+Yep.swift"; path = "Extensions/Double+Yep.swift"; sourceTree = ""; }; - 33180F571CC8887E00CACF38 /* LeftShareFeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftShareFeedCell.swift; sourceTree = ""; }; - 33180F591CC8889500CACF38 /* RightShareFeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightShareFeedCell.swift; sourceTree = ""; }; + 1CB66418C90B70CBF170F468 /* Pods-YepTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YepTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-YepTests/Pods-YepTests.debug.xcconfig"; sourceTree = ""; }; + 33180F5B1CC9C58100CACF38 /* DataSample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSample.swift; sourceTree = ""; }; + 336563F71CCF2FF800598378 /* LeftShareFeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftShareFeedCell.swift; sourceTree = ""; }; + 336563F91CCF300200598378 /* RightShareFeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightShareFeedCell.swift; sourceTree = ""; }; 33893E371CB7C62E00B276C1 /* NewFeed.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NewFeed.storyboard; sourceTree = ""; }; 33893E381CB7C62E00B276C1 /* NewFeedPreviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewFeedPreviewViewController.swift; sourceTree = ""; }; 33893E391CB7C62E00B276C1 /* NewFeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewFeedViewController.swift; sourceTree = ""; }; @@ -488,7 +512,6 @@ 50008D2C1CAB76AD00245296 /* SearchableItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchableItemType.swift; path = Realm/SearchableItemType.swift; sourceTree = ""; }; 50008D311CAE0D9A00245296 /* SearchConversationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchConversationsViewController.swift; path = ViewControllers/SearchConversations/SearchConversationsViewController.swift; sourceTree = ""; }; 50008D331CAE0DC700245296 /* SearchConversations.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SearchConversations.storyboard; path = ViewControllers/SearchConversations/SearchConversations.storyboard; sourceTree = ""; }; - 50008D351CAE114300245296 /* ConversationsSearchTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationsSearchTransition.swift; path = ViewControllers/Conversations/ConversationsSearchTransition.swift; sourceTree = ""; }; 50008D381CAE537400245296 /* SearchedFeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchedFeedCell.swift; path = Views/Cells/SearchedFeed/SearchedFeedCell.swift; sourceTree = ""; }; 50008D391CAE537400245296 /* SearchedFeedCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SearchedFeedCell.xib; path = Views/Cells/SearchedFeed/SearchedFeedCell.xib; sourceTree = ""; }; 5008A1941B8DB8B500E94274 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -519,6 +542,10 @@ 501690161BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftSocialWorkCell.swift; path = Views/Cells/ChatLeftSocialWork/ChatLeftSocialWorkCell.swift; sourceTree = ""; }; 501690171BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftSocialWorkCell.xib; path = Views/Cells/ChatLeftSocialWork/ChatLeftSocialWorkCell.xib; sourceTree = ""; }; 5016901B1BFB22180096C4F9 /* SocialWorkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocialWorkService.swift; path = Services/SocialWorkService.swift; sourceTree = ""; }; + 5017FC1A1CD095E100DFCB71 /* YepTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YepTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5017FC1C1CD095E100DFCB71 /* YepTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YepTests.swift; sourceTree = ""; }; + 5017FC1E1CD095E100DFCB71 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5017FC241CD0960200DFCB71 /* SyncTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncTests.swift; sourceTree = ""; }; 501813251BA07CED0025873C /* Activities.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Activities.xcassets; sourceTree = ""; }; 5018132B1BA3CFEF0025873C /* WeChatActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeChatActivity.swift; path = Activities/WeChatActivity.swift; sourceTree = ""; }; 502048231B0F1BB3002EBFC7 /* SearchedUsersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchedUsersViewController.swift; path = ViewControllers/SearchedUsers/SearchedUsersViewController.swift; sourceTree = ""; }; @@ -592,6 +619,10 @@ 505082321C21130100643CEB /* FeedGithubRepoContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedGithubRepoContainerView.swift; path = Views/ContainerViews/FeedGithubRepoContainerView.swift; sourceTree = ""; }; 505082341C212E9200643CEB /* FeedLocationContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedLocationContainerView.swift; path = Views/ContainerViews/FeedLocationContainerView.swift; sourceTree = ""; }; 505082361C21377E00643CEB /* FeedVoiceContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedVoiceContainerView.swift; path = Views/ContainerViews/FeedVoiceContainerView.swift; sourceTree = ""; }; + 505243A71CD983160094B471 /* ChangeMobileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChangeMobileViewController.swift; path = ViewControllers/ChangeMobile/ChangeMobileViewController.swift; sourceTree = ""; }; + 505243A91CD983390094B471 /* ChangeMobile.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = ChangeMobile.storyboard; path = ViewControllers/ChangeMobile/ChangeMobile.storyboard; sourceTree = ""; }; + 505243AC1CD9905C0094B471 /* VerifyChangedMobile.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = VerifyChangedMobile.storyboard; path = ViewControllers/VerifyChangedMobile/VerifyChangedMobile.storyboard; sourceTree = ""; }; + 505243AE1CD990750094B471 /* VerifyChangedMobileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VerifyChangedMobileViewController.swift; path = ViewControllers/VerifyChangedMobile/VerifyChangedMobileViewController.swift; sourceTree = ""; }; 5052620F1C52050700292C6A /* ConcurrentOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConcurrentOperation.swift; path = Operations/ConcurrentOperation.swift; sourceTree = ""; }; 505262111C5205AB00292C6A /* UploadAttachmentOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UploadAttachmentOperation.swift; path = Operations/UploadAttachmentOperation.swift; sourceTree = ""; }; 505262141C522A1E00292C6A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Show.storyboard; sourceTree = ""; }; @@ -636,8 +667,16 @@ 506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIFont+Yep.swift"; path = "Extensions/UIFont+Yep.swift"; sourceTree = ""; }; 507682EF1C9FE8F7006A973E /* SearchContactsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchContactsViewController.swift; path = ViewControllers/SearchContacts/SearchContactsViewController.swift; sourceTree = ""; }; 507682F21C9FE921006A973E /* SearchContacts.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SearchContacts.storyboard; path = ViewControllers/SearchContacts/SearchContacts.storyboard; sourceTree = ""; }; - 507682F41C9FED1F006A973E /* ContactsSearchTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactsSearchTransition.swift; path = ViewControllers/Contacts/ContactsSearchTransition.swift; sourceTree = ""; }; 5076C5691AC46B9F00B22952 /* FayeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FayeService.swift; path = Services/FayeService.swift; sourceTree = ""; }; + 5078130F1CD09FEE00F193AB /* ClearTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClearTests.swift; sourceTree = ""; }; + 507813111CD0A45800F193AB /* RealmTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmTests.swift; sourceTree = ""; }; + 507813131CD0B0CF00F193AB /* ServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTests.swift; sourceTree = ""; }; + 507813181CD1B01A00F193AB /* ShortcutItems.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShortcutItems.swift; path = Shortcuts/ShortcutItems.swift; sourceTree = ""; }; + 5078131A1CD1B04400F193AB /* ShortcutType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShortcutType.swift; path = Shortcuts/ShortcutType.swift; sourceTree = ""; }; + 5078131C1CD1E33A00F193AB /* OpenGraphTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGraphTests.swift; sourceTree = ""; }; + 5078131E1CD1EB5000F193AB /* NSURL+YepTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSURL+YepTests.swift"; path = "Extensions/NSURL+YepTests.swift"; sourceTree = ""; }; + 507813211CD2F9C100F193AB /* DeviceGuru+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "DeviceGuru+Yep.swift"; path = "Extensions/DeviceGuru+Yep.swift"; sourceTree = ""; }; + 507813231CD2FEF400F193AB /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 507CF1671AFC84A500E261B4 /* CustomNavigationBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomNavigationBarViewController.swift; path = ViewControllers/CustomNavigationBar/CustomNavigationBarViewController.swift; sourceTree = ""; }; 507F65F61B2EBC2700A35FDC /* MediaPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaPreviewView.swift; path = Views/Media/MediaPreviewView.swift; sourceTree = ""; }; 5080EB1B1B93F43A006F3C2D /* PodsHelpYepViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PodsHelpYepViewController.swift; path = ViewControllers/PodsHelpYep/PodsHelpYepViewController.swift; sourceTree = ""; }; @@ -683,6 +722,7 @@ 5099B9C61AB99F7B002F940B /* RegisterPickAvatarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RegisterPickAvatarViewController.swift; path = ViewControllers/Register/RegisterPickAvatarViewController.swift; sourceTree = ""; }; 5099B9C81AB9A7CB002F940B /* CameraPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CameraPreviewView.swift; path = Views/CameraPreview/CameraPreviewView.swift; sourceTree = ""; }; 509B120E1C436E7C0000BE71 /* MentionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MentionView.swift; path = Views/Mention/MentionView.swift; sourceTree = ""; }; + 509F12941CCF14130073CB42 /* XCUIElement+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "XCUIElement+Yep.swift"; path = "Extensions/XCUIElement+Yep.swift"; sourceTree = ""; }; 50A0E3001C3E18A10025BEFC /* CLLocationCoordinate2D+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CLLocationCoordinate2D+Yep.swift"; path = "Extensions/CLLocationCoordinate2D+Yep.swift"; sourceTree = ""; }; 50A1FA751C0D47D300EEDA74 /* SubscribeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeView.swift; path = Views/Subscribe/SubscribeView.swift; sourceTree = ""; }; 50A640DF1B6F38CF0050E4A3 /* NotificationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NotificationsViewController.swift; path = ViewControllers/Notifications/NotificationsViewController.swift; sourceTree = ""; }; @@ -712,6 +752,9 @@ 50B38FCB1B6B37F00064C132 /* FeedbackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedbackViewController.swift; path = ViewControllers/Feedback/FeedbackViewController.swift; sourceTree = ""; }; 50B3C94F1CC8649500623E29 /* SearchedFeedAnyImagesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchedFeedAnyImagesCell.swift; path = Views/Cells/Feed/SearchedFeed/SearchedFeedAnyImagesCell.swift; sourceTree = ""; }; 50B3C9511CC873F000623E29 /* SearchFeedsFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchFeedsFooterView.swift; path = Views/Footers/SearchFeedsFooterView.swift; sourceTree = ""; }; + 50B3C9551CC9B66A00623E29 /* SearchTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchTransition.swift; path = Transitions/SearchTransition.swift; sourceTree = ""; }; + 50B3C9571CC9BA9000623E29 /* SearchTriggerRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchTriggerRepresentation.swift; path = Protocols/SearchTriggerRepresentation.swift; sourceTree = ""; }; + 50B3C9591CC9BB2F00623E29 /* SearchActionRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchActionRepresentation.swift; path = Protocols/SearchActionRepresentation.swift; sourceTree = ""; }; 50B6A03A1CA27A6000C460E3 /* TableSectionTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableSectionTitleView.swift; path = Views/ReusableViews/TableSectionTitle/TableSectionTitleView.swift; sourceTree = ""; }; 50B6A03E1CA282B100C460E3 /* ActionSheetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetView.swift; sourceTree = ""; }; 50B6A0401CA395A200C460E3 /* SearchedContactsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchedContactsCell.swift; path = Views/Cells/Contacts/SearchedContactsCell.swift; sourceTree = ""; }; @@ -741,6 +784,9 @@ 50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftImageCell.swift; path = Views/Cells/ChatLeftImage/ChatLeftImageCell.swift; sourceTree = ""; }; 50D45FC21C44D2300044C95A /* OpenGraphService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OpenGraphService.swift; path = Services/OpenGraphService.swift; sourceTree = ""; }; 50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaControlView.swift; path = Views/Media/MediaControlView.swift; sourceTree = ""; }; + 50D50AC01CCE01D300FB5753 /* YepUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YepUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 50D50AC21CCE01D300FB5753 /* YepUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YepUITests.swift; sourceTree = ""; }; + 50D50AC41CCE01D300FB5753 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50DBDEF01C48C2240044E21B /* OpenGraphInfoType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OpenGraphInfoType.swift; path = Protocols/OpenGraphInfoType.swift; sourceTree = ""; }; 50DBDEFB1C4CCB030044E21B /* ChatLeftTextURLCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftTextURLCell.swift; path = Views/Cells/ChatLeftTextURL/ChatLeftTextURLCell.swift; sourceTree = ""; }; 50DBDEFF1C4CE75E0044E21B /* ChatRightTextURLCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightTextURLCell.swift; path = Views/Cells/ChatRightTextURL/ChatRightTextURLCell.swift; sourceTree = ""; }; @@ -792,7 +838,6 @@ 50E34C201CB75774007CAE80 /* SearchedDiscoveredUserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SearchedDiscoveredUserCell.xib; path = Views/Cells/SearchedDiscoveredUser/SearchedDiscoveredUserCell.xib; sourceTree = ""; }; 50E34C251CBB8A37007CAE80 /* SearchFeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchFeedsViewController.swift; path = ViewControllers/SearchFeeds/SearchFeedsViewController.swift; sourceTree = ""; }; 50E34C271CBB8A49007CAE80 /* SearchFeeds.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SearchFeeds.storyboard; path = ViewControllers/SearchFeeds/SearchFeeds.storyboard; sourceTree = ""; }; - 50E34C291CBB8CAA007CAE80 /* FeedsSearchTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedsSearchTransition.swift; path = ViewControllers/Feeds/FeedsSearchTransition.swift; sourceTree = ""; }; 50E34C2B1CBDEC31007CAE80 /* FeedsMoreViewManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedsMoreViewManager.swift; path = ViewControllers/Feeds/FeedsMoreViewManager.swift; sourceTree = ""; }; 50E34C2E1CBE2A33007CAE80 /* CreatorsOfBlockedFeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CreatorsOfBlockedFeedsViewController.swift; path = ViewControllers/CreatorsOfBlockedFeeds/CreatorsOfBlockedFeedsViewController.swift; sourceTree = ""; }; 50E34C301CBE2A48007CAE80 /* CreatorsOfBlockedFeeds.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = CreatorsOfBlockedFeeds.storyboard; path = ViewControllers/CreatorsOfBlockedFeeds/CreatorsOfBlockedFeeds.storyboard; sourceTree = ""; }; @@ -856,6 +901,7 @@ 92E0FFE51C5F38E400226B96 /* MZFayeMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MZFayeMessage.h; sourceTree = ""; }; 92E0FFE61C5F38E400226B96 /* MZFayeMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MZFayeMessage.m; sourceTree = ""; }; C3146F0F60F2A38BC6E0FA66 /* Pods_Yep.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Yep.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F2C540EC0FBAC0042492703B /* Pods-YepTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YepTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-YepTests/Pods-YepTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -883,7 +929,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 0A8F54BD1AB67D11004AD60E /* Frameworks */ = { + 5017FC171CD095E100DFCB71 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50D50ABD1CCE01D300FB5753 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -939,7 +992,8 @@ isa = PBXGroup; children = ( 0A8F54AD1AB67D11004AD60E /* Yep */, - 0A8F54C31AB67D11004AD60E /* YepTests */, + 50D50AC11CCE01D300FB5753 /* YepUITests */, + 5017FC1B1CD095E100DFCB71 /* YepTests */, 0A8F54AC1AB67D11004AD60E /* Products */, 59CC1DE9D59AC08247B97B4F /* Pods */, 67B708B60AAD1DEA82A6A682 /* Frameworks */, @@ -950,7 +1004,8 @@ isa = PBXGroup; children = ( 0A8F54AB1AB67D11004AD60E /* Yep.app */, - 0A8F54C01AB67D11004AD60E /* YepTests.xctest */, + 50D50AC01CCE01D300FB5753 /* YepUITests.xctest */, + 5017FC1A1CD095E100DFCB71 /* YepTests.xctest */, ); name = Products; sourceTree = ""; @@ -959,8 +1014,9 @@ isa = PBXGroup; children = ( 848806B21B7B0230000C5424 /* Yep.entitlements */, - 0A8F54B01AB67D11004AD60E /* AppDelegate.swift */, 0AD7EA581AC3EAF300617758 /* Yep-Bridging-Header.h */, + 0A8F54B01AB67D11004AD60E /* AppDelegate.swift */, + 507813171CD1AFB000F193AB /* Shortcuts */, 92E0FFE11C5F38E400226B96 /* Vendors */, 50AFBF4F1BB3A5A1007D19DA /* Sounds */, 50E114F51ABBF9D100F13000 /* Realm */, @@ -973,6 +1029,7 @@ 5052620E1C5202C700292C6A /* Operations */, 504F5E721BA034BB006CE418 /* Activities */, 5023DE391AB6979C00B3EE96 /* Views */, + 50B3C9541CC9B62600623E29 /* Transitions */, 5023DE271AB6856600B3EE96 /* ViewControllers */, 502AE5171AB70D72005BD199 /* Intro.storyboard */, 505262131C522A1E00292C6A /* Show.storyboard */, @@ -999,23 +1056,6 @@ name = "Supporting Files"; sourceTree = ""; }; - 0A8F54C31AB67D11004AD60E /* YepTests */ = { - isa = PBXGroup; - children = ( - 0A8F54C61AB67D11004AD60E /* YepTests.swift */, - 0A8F54C41AB67D11004AD60E /* Supporting Files */, - ); - path = YepTests; - sourceTree = ""; - }; - 0A8F54C41AB67D11004AD60E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 0A8F54C51AB67D11004AD60E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; 0A9018801B01152D00AE4B7F /* ProfileSocialAccount */ = { isa = PBXGroup; children = ( @@ -1063,19 +1103,19 @@ name = ScrollView; sourceTree = ""; }; - 33180F4C1CC8867600CACF38 /* ChatRightShareFeed */ = { + 336563F51CCF2F2C00598378 /* ChatRightShareFeed */ = { isa = PBXGroup; children = ( - 33180F591CC8889500CACF38 /* RightShareFeedCell.swift */, + 336563F91CCF300200598378 /* RightShareFeedCell.swift */, ); name = ChatRightShareFeed; path = Views/Cells/ChatRightShareFeed; sourceTree = ""; }; - 33180F4E1CC886BF00CACF38 /* ChatLeftShareFeed */ = { + 336563F61CCF2F7000598378 /* ChatLeftShareFeed */ = { isa = PBXGroup; children = ( - 33180F571CC8887E00CACF38 /* LeftShareFeedCell.swift */, + 336563F71CCF2FF800598378 /* LeftShareFeedCell.swift */, ); name = ChatLeftShareFeed; path = Views/Cells/ChatLeftShareFeed; @@ -1209,6 +1249,22 @@ name = ChatLeftSocialWork; sourceTree = ""; }; + 5017FC1B1CD095E100DFCB71 /* YepTests */ = { + isa = PBXGroup; + children = ( + 5017FC1C1CD095E100DFCB71 /* YepTests.swift */, + 507813131CD0B0CF00F193AB /* ServiceTests.swift */, + 507813111CD0A45800F193AB /* RealmTests.swift */, + 5017FC241CD0960200DFCB71 /* SyncTests.swift */, + 5078130F1CD09FEE00F193AB /* ClearTests.swift */, + 5078131C1CD1E33A00F193AB /* OpenGraphTests.swift */, + 507813231CD2FEF400F193AB /* Images.xcassets */, + 507813201CD1EB5400F193AB /* Extensions */, + 5017FC1E1CD095E100DFCB71 /* Info.plist */, + ); + path = YepTests; + sourceTree = ""; + }; 502048251B0F1BB8002EBFC7 /* SearchedUsers */ = { isa = PBXGroup; children = ( @@ -1260,6 +1316,8 @@ 50894CF21AEA472B000981AF /* Settings */, 50894CFF1AEA5A7E000981AF /* EditProfile */, 500A0B1D1B450C2F00D49358 /* EditNicknameAndBadge */, + 505243A61CD982C60094B471 /* ChangeMobile */, + 505243AB1CD98B7D0094B471 /* VerifyChangedMobile */, 50A640E11B6F38D60050E4A3 /* Notifications */, 50A640ED1B6F4E480050E4A3 /* DoNotDisturbPeriod */, 503ED23B1B8ADCED0072187D /* BlackList */, @@ -1288,7 +1346,6 @@ isa = PBXGroup; children = ( 5023DE251AB6856300B3EE96 /* ConversationsViewController.swift */, - 50008D351CAE114300245296 /* ConversationsSearchTransition.swift */, ); name = Conversations; sourceTree = ""; @@ -1297,7 +1354,6 @@ isa = PBXGroup; children = ( 5023DE291AB685BA00B3EE96 /* ContactsViewController.swift */, - 507682F41C9FED1F006A973E /* ContactsSearchTransition.swift */, ); name = Contacts; sourceTree = ""; @@ -1351,6 +1407,7 @@ 500D35E01C3A5134006D17E3 /* PHImageRequestOptions+Yep.swift */, 50A0E3001C3E18A10025BEFC /* CLLocationCoordinate2D+Yep.swift */, 50E34C1A1CB60475007CAE80 /* UISearchBar+Yep.swift */, + 507813211CD2F9C100F193AB /* DeviceGuru+Yep.swift */, ); name = Extensions; sourceTree = ""; @@ -1398,8 +1455,8 @@ 5023DE3A1AB697A700B3EE96 /* Cells */ = { isa = PBXGroup; children = ( - 33180F4E1CC886BF00CACF38 /* ChatLeftShareFeed */, - 33180F4C1CC8867600CACF38 /* ChatRightShareFeed */, + 336563F61CCF2F7000598378 /* ChatLeftShareFeed */, + 336563F51CCF2F2C00598378 /* ChatRightShareFeed */, 338940341CB7C80900B276C1 /* NewFeedPreview */, 848806AD1B7899E6000C5424 /* EditSkill */, 50A640E21B6F41340050E4A3 /* DoNotDisturb */, @@ -1712,6 +1769,7 @@ isa = PBXGroup; children = ( 503CAB421AC00CB100DFE830 /* ConversationViewController.swift */, + 33180F5B1CC9C58100CACF38 /* DataSample.swift */, 500EED971BF07A3000C6BABC /* ConversationViewController+Feed.swift */, 50E34C321CBF7E2A007CAE80 /* ConversationViewController+TextIndicator.swift */, 50165C441AC2860900C7AEBE /* ConversationLayout.swift */, @@ -1756,6 +1814,24 @@ name = Activities; sourceTree = ""; }; + 505243A61CD982C60094B471 /* ChangeMobile */ = { + isa = PBXGroup; + children = ( + 505243A71CD983160094B471 /* ChangeMobileViewController.swift */, + 505243A91CD983390094B471 /* ChangeMobile.storyboard */, + ); + name = ChangeMobile; + sourceTree = ""; + }; + 505243AB1CD98B7D0094B471 /* VerifyChangedMobile */ = { + isa = PBXGroup; + children = ( + 505243AE1CD990750094B471 /* VerifyChangedMobileViewController.swift */, + 505243AC1CD9905C0094B471 /* VerifyChangedMobile.storyboard */, + ); + name = VerifyChangedMobile; + sourceTree = ""; + }; 5052620E1C5202C700292C6A /* Operations */ = { isa = PBXGroup; children = ( @@ -1900,6 +1976,23 @@ name = SearchContacts; sourceTree = ""; }; + 507813171CD1AFB000F193AB /* Shortcuts */ = { + isa = PBXGroup; + children = ( + 5078131A1CD1B04400F193AB /* ShortcutType.swift */, + 507813181CD1B01A00F193AB /* ShortcutItems.swift */, + ); + name = Shortcuts; + sourceTree = ""; + }; + 507813201CD1EB5400F193AB /* Extensions */ = { + isa = PBXGroup; + children = ( + 5078131E1CD1EB5000F193AB /* NSURL+YepTests.swift */, + ); + name = Extensions; + sourceTree = ""; + }; 507CF1691AFC84AA00E261B4 /* CustomNavigationBar */ = { isa = PBXGroup; children = ( @@ -2093,6 +2186,14 @@ name = Mention; sourceTree = ""; }; + 509F12961CCF14190073CB42 /* Extensions */ = { + isa = PBXGroup; + children = ( + 509F12941CCF14130073CB42 /* XCUIElement+Yep.swift */, + ); + name = Extensions; + sourceTree = ""; + }; 50A1FA771C0D47D900EEDA74 /* Subscribe */ = { isa = PBXGroup; children = ( @@ -2196,7 +2297,6 @@ children = ( 50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */, 50E34C2B1CBDEC31007CAE80 /* FeedsMoreViewManager.swift */, - 50E34C291CBB8CAA007CAE80 /* FeedsSearchTransition.swift */, ); name = Feeds; sourceTree = ""; @@ -2244,6 +2344,14 @@ name = Footers; sourceTree = ""; }; + 50B3C9541CC9B62600623E29 /* Transitions */ = { + isa = PBXGroup; + children = ( + 50B3C9551CC9B66A00623E29 /* SearchTransition.swift */, + ); + name = Transitions; + sourceTree = ""; + }; 50B6A03C1CA27A6400C460E3 /* TableSectionTitle */ = { isa = PBXGroup; children = ( @@ -2326,11 +2434,23 @@ name = ChatLeftImage; sourceTree = ""; }; + 50D50AC11CCE01D300FB5753 /* YepUITests */ = { + isa = PBXGroup; + children = ( + 50D50AC21CCE01D300FB5753 /* YepUITests.swift */, + 509F12961CCF14190073CB42 /* Extensions */, + 50D50AC41CCE01D300FB5753 /* Info.plist */, + ); + path = YepUITests; + sourceTree = ""; + }; 50DBDEF21C48C2290044E21B /* Protocols */ = { isa = PBXGroup; children = ( 50DBDEF01C48C2240044E21B /* OpenGraphInfoType.swift */, 50E34C1C1CB7516C007CAE80 /* UserRepresentation.swift */, + 50B3C9571CC9BA9000623E29 /* SearchTriggerRepresentation.swift */, + 50B3C9591CC9BB2F00623E29 /* SearchActionRepresentation.swift */, ); name = Protocols; sourceTree = ""; @@ -2582,6 +2702,8 @@ children = ( 88C96122C478C4D134F634F6 /* Pods-Yep.debug.xcconfig */, 4EEAF88E6C6D61E595249DF5 /* Pods-Yep.release.xcconfig */, + 1CB66418C90B70CBF170F468 /* Pods-YepTests.debug.xcconfig */, + F2C540EC0FBAC0042492703B /* Pods-YepTests.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -2606,6 +2728,7 @@ 00E9240F12DD22E06A35F782 /* AssetsLibrary.framework */, 34A6628BE8C7FFA96376C5C6 /* CoreText.framework */, 4C73250AC8BB116924CA11C0 /* QuartzCore.framework */, + 0A884441143C544F0CD78CF8 /* Pods_YepTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -2735,24 +2858,42 @@ productReference = 0A8F54AB1AB67D11004AD60E /* Yep.app */; productType = "com.apple.product-type.application"; }; - 0A8F54BF1AB67D11004AD60E /* YepTests */ = { + 5017FC191CD095E100DFCB71 /* YepTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 0A8F54CD1AB67D11004AD60E /* Build configuration list for PBXNativeTarget "YepTests" */; + buildConfigurationList = 5017FC231CD095E100DFCB71 /* Build configuration list for PBXNativeTarget "YepTests" */; buildPhases = ( - 0A8F54BC1AB67D11004AD60E /* Sources */, - 0A8F54BD1AB67D11004AD60E /* Frameworks */, - 0A8F54BE1AB67D11004AD60E /* Resources */, + 5017FC161CD095E100DFCB71 /* Sources */, + 5017FC171CD095E100DFCB71 /* Frameworks */, + 5017FC181CD095E100DFCB71 /* Resources */, ); buildRules = ( ); dependencies = ( - 0A8F54C21AB67D11004AD60E /* PBXTargetDependency */, + 5017FC201CD095E100DFCB71 /* PBXTargetDependency */, ); name = YepTests; productName = YepTests; - productReference = 0A8F54C01AB67D11004AD60E /* YepTests.xctest */; + productReference = 5017FC1A1CD095E100DFCB71 /* YepTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 50D50ABF1CCE01D300FB5753 /* YepUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50D50AC91CCE01D300FB5753 /* Build configuration list for PBXNativeTarget "YepUITests" */; + buildPhases = ( + 50D50ABC1CCE01D300FB5753 /* Sources */, + 50D50ABD1CCE01D300FB5753 /* Frameworks */, + 50D50ABE1CCE01D300FB5753 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 50D50AC61CCE01D300FB5753 /* PBXTargetDependency */, + ); + name = YepUITests; + productName = YepUITests; + productReference = 50D50AC01CCE01D300FB5753 /* YepUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -2760,7 +2901,7 @@ isa = PBXProject; attributes = { LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 0700; + LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 0700; ORGANIZATIONNAME = "Catch Inc"; TargetAttributes = { @@ -2782,8 +2923,12 @@ }; }; }; - 0A8F54BF1AB67D11004AD60E = { - CreatedOnToolsVersion = 6.3; + 5017FC191CD095E100DFCB71 = { + CreatedOnToolsVersion = 7.3; + TestTargetID = 0A8F54AA1AB67D11004AD60E; + }; + 50D50ABF1CCE01D300FB5753 = { + CreatedOnToolsVersion = 7.3; TestTargetID = 0A8F54AA1AB67D11004AD60E; }; }; @@ -2803,7 +2948,8 @@ projectRoot = ""; targets = ( 0A8F54AA1AB67D11004AD60E /* Yep */, - 0A8F54BF1AB67D11004AD60E /* YepTests */, + 50D50ABF1CCE01D300FB5753 /* YepUITests */, + 5017FC191CD095E100DFCB71 /* YepTests */, ); }; /* End PBXProject section */ @@ -2820,6 +2966,7 @@ 50AACECD1BB5419A004DD54B /* FeedView.xib in Resources */, 50DEE0611BEA146900015741 /* Conversation.storyboard in Resources */, 50C090A11ADE391600CC6389 /* AddSkillsReusableView.xib in Resources */, + 505243AD1CD9905C0094B471 /* VerifyChangedMobile.storyboard in Resources */, 501690191BFB18D20096C4F9 /* ChatLeftSocialWorkCell.xib in Resources */, 504F415B1AD5028200FBB19A /* SkillCell.xib in Resources */, 50DEE0531BEA0AAC00015741 /* DoNotDisturbPeriod.storyboard in Resources */, @@ -2841,6 +2988,7 @@ 508F8FD11B01D04400461B0B /* GithubRepoCell.xib in Resources */, 6A5632941BC911CB00397B53 /* DiscoverNormalUserCell.xib in Resources */, 500EED9D1BF1B84A00C6BABC /* MediaPreview.storyboard in Resources */, + 505243AA1CD983390094B471 /* ChangeMobile.storyboard in Resources */, 50DEE0591BEA0D7B00015741 /* EditSkills.storyboard in Resources */, 50DEE0551BEA0BB200015741 /* Notifications.storyboard in Resources */, 508F8FD81B01EDB400461B0B /* DribbbleShotCell.xib in Resources */, @@ -2919,7 +3067,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 0A8F54BE1AB67D11004AD60E /* Resources */ = { + 5017FC181CD095E100DFCB71 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 507813241CD2FEF400F193AB /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50D50ABE1CCE01D300FB5753 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -2999,6 +3155,7 @@ 5099B9C41AB9510D002F940B /* RankView.swift in Sources */, 507682F01C9FE8F7006A973E /* SearchContactsViewController.swift in Sources */, 502AE5311AB85067005BD199 /* RegisterPickMobileViewController.swift in Sources */, + 507813221CD2F9C100F193AB /* DeviceGuru+Yep.swift in Sources */, 50B22E461C588B6200219625 /* CGImage+Yep.swift in Sources */, 50535D1A1BCD19AE002FE1A4 /* TouchClosuresView.swift in Sources */, 506E241E1AE6391A00A33C1A /* TouchZoomButton.swift in Sources */, @@ -3032,6 +3189,7 @@ 5023DE331AB688F600B3EE96 /* UIColor+Yep.swift in Sources */, 503315971AC1017E0008A209 /* ChatLeftTextCell.swift in Sources */, 509B120F1C436E7C0000BE71 /* MentionView.swift in Sources */, + 50B3C9581CC9BA9000623E29 /* SearchTriggerRepresentation.swift in Sources */, 50CB05801CC5E1200024BE06 /* SearchedFeedLocationCell.swift in Sources */, 505262101C52050700292C6A /* ConcurrentOperation.swift in Sources */, 50B2CA941BD0EF7900668AC6 /* MoreMessageTypesView.swift in Sources */, @@ -3044,6 +3202,7 @@ 50B2CA921BD0BF3800668AC6 /* FeedTextView.swift in Sources */, 6A649A9B1BB259CB00EE4FB5 /* YepSoundEffect.swift in Sources */, 50D45FC31C44D2300044C95A /* OpenGraphService.swift in Sources */, + 507813191CD1B01A00F193AB /* ShortcutItems.swift in Sources */, 508D174C1ACCF49B0092D666 /* ChatRightAudioCell.swift in Sources */, 502AE53D1AB87C0D005BD199 /* YepUserDefaults.swift in Sources */, 50F5AF881BBBB2AE0033C9BC /* HorizontalLineView.swift in Sources */, @@ -3057,6 +3216,7 @@ 50894D021AEA6433000981AF /* EditProfileLessInfoCell.swift in Sources */, 501138261BCB59120055A014 /* FeedConversationsViewController.swift in Sources */, 50CB05781CC5D6A50024BE06 /* SearchedFeedNormalImagesCell.swift in Sources */, + 336563F81CCF2FF800598378 /* LeftShareFeedCell.swift in Sources */, 50894CFA1AEA54BA000981AF /* SettingsMoreCell.swift in Sources */, 50AFBF4B1BB2A44C007D19DA /* InfoView.swift in Sources */, 50DEE03D1BE70E1500015741 /* UIScrollView+Yep.swift in Sources */, @@ -3072,6 +3232,7 @@ 92E0FFE71C5F38E400226B96 /* MZFayeClient.m in Sources */, 50B3C9501CC8649500623E29 /* SearchedFeedAnyImagesCell.swift in Sources */, 50CB05841CC5E39A0024BE06 /* IconTitleContainerView.swift in Sources */, + 50B3C9561CC9B66A00623E29 /* SearchTransition.swift in Sources */, 5080EB1C1B93F43A006F3C2D /* PodsHelpYepViewController.swift in Sources */, 0A1CAC731AFA526200826B45 /* SkillHomeSectionButton.swift in Sources */, 50E392BC1C22752C00C0CECB /* FeedNormalImagesCell.swift in Sources */, @@ -3080,7 +3241,6 @@ 50CB05761CC5D0FD0024BE06 /* SearchedFeedBasicCell.swift in Sources */, 507F65F71B2EBC2700A35FDC /* MediaPreviewView.swift in Sources */, 501690181BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift in Sources */, - 50008D361CAE114300245296 /* ConversationsSearchTransition.swift in Sources */, 50E114F41ABBF9CC00F13000 /* Models.swift in Sources */, 848806B01B789A2D000C5424 /* EditSkillCell.swift in Sources */, 5018132C1BA3CFEF0025873C /* WeChatActivity.swift in Sources */, @@ -3120,7 +3280,6 @@ 50EB8CBF1AE89ED8001AC1EE /* ChatLeftVideoCell.swift in Sources */, 502AE5331AB86020005BD199 /* RegisterVerifyMobileViewController.swift in Sources */, 92E0FFE81C5F38E400226B96 /* MZFayeMessage.m in Sources */, - 33180F5A1CC8889500CACF38 /* RightShareFeedCell.swift in Sources */, 5064F6541B0B0E160089FAD4 /* AddFriendSearchCell.swift in Sources */, 504CBA3D1B3D04B800663A90 /* ChatTextView.swift in Sources */, 0A1CAC6E1AFA42D400826B45 /* SkillHomeViewController.swift in Sources */, @@ -3149,6 +3308,7 @@ 5028C5D41CA8CF9300879A95 /* SendingMessagePool.swift in Sources */, 50C0909D1ADE294800CC6389 /* RegisterPickSkillsLayout.swift in Sources */, 50E3488E1B905C0100A8E699 /* AboutViewController.swift in Sources */, + 336563FA1CCF300200598378 /* RightShareFeedCell.swift in Sources */, 338940371CB7C80900B276C1 /* NewFeedPreviewCell.swift in Sources */, 502AE52E1AB83879005BD199 /* UnderLineTextField.swift in Sources */, 5012FDDA1BFABDE400294145 /* ProfileFeedsCell.swift in Sources */, @@ -3180,7 +3340,6 @@ 502AE5271AB72D06005BD199 /* YepNetworking.swift in Sources */, 5076C56A1AC46B9F00B22952 /* FayeService.swift in Sources */, 505EC8131ADE607D001D27E0 /* SkillCategoryButton.swift in Sources */, - 50E34C2A1CBB8CAA007CAE80 /* FeedsSearchTransition.swift in Sources */, 5064F6591B0B14420089FAD4 /* AddFriendMoreCell.swift in Sources */, 848806B61B7B4F53000C5424 /* ShowViewController.swift in Sources */, 5099B9C71AB99F7B002F940B /* RegisterPickAvatarViewController.swift in Sources */, @@ -3231,11 +3390,11 @@ 50E34C331CBF7E2A007CAE80 /* ConversationViewController+TextIndicator.swift in Sources */, 50FE29CF1C478A4A0067CA03 /* FeedURLContainerView.swift in Sources */, 50CC4FA41C1AC58700B6A263 /* UICollectionView+Yep.swift in Sources */, + 5078131B1CD1B04400F193AB /* ShortcutType.swift in Sources */, 50E34C111CB4B66D007CAE80 /* SearchedUserMessagesViewController.swift in Sources */, 0A271FD71ACA055400822DFC /* YepAudioService.swift in Sources */, 5064F6501B0AE2E60089FAD4 /* AddFriendsViewController.swift in Sources */, 505082331C21130100643CEB /* FeedGithubRepoContainerView.swift in Sources */, - 33180F581CC8887E00CACF38 /* LeftShareFeedCell.swift in Sources */, 33893E3C1CB7C62E00B276C1 /* NewFeedViewController.swift in Sources */, 507CF1681AFC84A500E261B4 /* CustomNavigationBarViewController.swift in Sources */, 0A3E60601ACCA9C0003DC853 /* String+Yep.swift in Sources */, @@ -3243,10 +3402,12 @@ 503315A01AC126100008A209 /* MessageToolbar.swift in Sources */, 508B717D1B1FE9B6006BDDB3 /* ChatRightBaseCell.swift in Sources */, 50A9D44B1ACD37A8000B2599 /* NSFileManager+Yep.swift in Sources */, + 505243AF1CD990750094B471 /* VerifyChangedMobileViewController.swift in Sources */, 338940491CBCF05A00B276C1 /* AlbumListCell.swift in Sources */, 502AE5531AB93215005BD199 /* ProfileFooterCell.swift in Sources */, 50392CD11B393FF60009D647 /* SkillAddCell.swift in Sources */, 50EB8CCB1AEA1AC0001AC1EE /* MediaView.swift in Sources */, + 505243A81CD983160094B471 /* ChangeMobileViewController.swift in Sources */, 506923121ADE025200D27574 /* Waver.swift in Sources */, 504F5E6F1B9F1F13006CE418 /* NSURLRequest+cURL.swift in Sources */, 50E34C2C1CBDEC31007CAE80 /* FeedsMoreViewManager.swift in Sources */, @@ -3262,6 +3423,7 @@ 50DBDEF11C48C2240044E21B /* OpenGraphInfoType.swift in Sources */, 50894CF51AEA4A01000981AF /* SettingsUserCell.swift in Sources */, 84385C441B8592120071130D /* ShowStepGeniusViewController.swift in Sources */, + 50B3C95A1CC9BB2F00623E29 /* SearchActionRepresentation.swift in Sources */, 6A5632931BC911CB00397B53 /* DiscoverNormalUserCell.swift in Sources */, 5053AD5A1AF83B4200B3CBFA /* ChatLeftLocationCell.swift in Sources */, 50A93A9D1BD8C515007005DB /* FeedSkillPickerItemView.swift in Sources */, @@ -3279,8 +3441,8 @@ 84385C481B85AC890071130D /* ShowStepMatchViewController.swift in Sources */, 50894D0C1AEE0177000981AF /* EditProfileColoredTitleCell.swift in Sources */, 5053AD501AF82A1700B3CBFA /* PickLocationCell.swift in Sources */, - 507682F51C9FED1F006A973E /* ContactsSearchTransition.swift in Sources */, 50CB057E1CC5DC260024BE06 /* SearchedFeedVoiceCell.swift in Sources */, + 33180F5C1CC9C58100CACF38 /* DataSample.swift in Sources */, 5023DE521AB6C51300B3EE96 /* CGRect+Yep.swift in Sources */, 5053AD4A1AF772C500B3CBFA /* PickLocationViewController.swift in Sources */, 502B60681BD6252B00D9C17E /* UserAvatar.swift in Sources */, @@ -3288,21 +3450,41 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 0A8F54BC1AB67D11004AD60E /* Sources */ = { + 5017FC161CD095E100DFCB71 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5017FC251CD0960200DFCB71 /* SyncTests.swift in Sources */, + 507813101CD09FEE00F193AB /* ClearTests.swift in Sources */, + 507813141CD0B0CF00F193AB /* ServiceTests.swift in Sources */, + 5017FC1D1CD095E100DFCB71 /* YepTests.swift in Sources */, + 5078131D1CD1E33A00F193AB /* OpenGraphTests.swift in Sources */, + 5078131F1CD1EB5000F193AB /* NSURL+YepTests.swift in Sources */, + 507813121CD0A45800F193AB /* RealmTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50D50ABC1CCE01D300FB5753 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0A8F54C71AB67D11004AD60E /* YepTests.swift in Sources */, + 509F12951CCF14130073CB42 /* XCUIElement+Yep.swift in Sources */, + 50D50AC31CCE01D300FB5753 /* YepUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 0A8F54C21AB67D11004AD60E /* PBXTargetDependency */ = { + 5017FC201CD095E100DFCB71 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 0A8F54AA1AB67D11004AD60E /* Yep */; - targetProxy = 0A8F54C11AB67D11004AD60E /* PBXContainerItemProxy */; + targetProxy = 5017FC1F1CD095E100DFCB71 /* PBXContainerItemProxy */; + }; + 50D50AC61CCE01D300FB5753 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0A8F54AA1AB67D11004AD60E /* Yep */; + targetProxy = 50D50AC51CCE01D300FB5753 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -3380,7 +3562,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -3440,6 +3622,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-D JPUSH"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; @@ -3454,7 +3637,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Yep", @@ -3627,39 +3810,62 @@ }; name = Release; }; - 0A8F54CE1AB67D11004AD60E /* Debug */ = { + 5017FC211CD095E100DFCB71 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ""; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); + CLANG_ANALYZER_NONNULL = YES; + DEBUG_INFORMATION_FORMAT = dwarf; INFOPLIST_FILE = YepTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - OTHER_SWIFT_FLAGS = "-D DEBUG -D STAGING_"; - PRODUCT_BUNDLE_IDENTIFIER = "Catch-Inc.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "Catch-Inc.YepTests"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Yep.app/Yep"; }; name = Debug; }; - 0A8F54CF1AB67D11004AD60E /* Release */ = { + 5017FC221CD095E100DFCB71 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ""; + CLANG_ANALYZER_NONNULL = YES; INFOPLIST_FILE = YepTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "Catch-Inc.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "Catch-Inc.YepTests"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Yep.app/Yep"; }; name = Release; }; + 50D50AC71CCE01D300FB5753 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + INFOPLIST_FILE = YepUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nixWork.YepUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Yep; + }; + name = Debug; + }; + 50D50AC81CCE01D300FB5753 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + INFOPLIST_FILE = YepUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nixWork.YepUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Yep; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -3681,11 +3887,20 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 0A8F54CD1AB67D11004AD60E /* Build configuration list for PBXNativeTarget "YepTests" */ = { + 5017FC231CD095E100DFCB71 /* Build configuration list for PBXNativeTarget "YepTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5017FC211CD095E100DFCB71 /* Debug */, + 5017FC221CD095E100DFCB71 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50D50AC91CCE01D300FB5753 /* Build configuration list for PBXNativeTarget "YepUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0A8F54CE1AB67D11004AD60E /* Debug */, - 0A8F54CF1AB67D11004AD60E /* Release */, + 50D50AC71CCE01D300FB5753 /* Debug */, + 50D50AC81CCE01D300FB5753 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Yep/Activities/WeChatActivity.swift b/Yep/Activities/WeChatActivity.swift index 9fcf958d7..323cc2c75 100644 --- a/Yep/Activities/WeChatActivity.swift +++ b/Yep/Activities/WeChatActivity.swift @@ -9,7 +9,7 @@ import UIKit import MonkeyKing -class WeChatActivity: AnyActivity { +final class WeChatActivity: AnyActivity { enum Type { diff --git a/Yep/AlbumListController.swift b/Yep/AlbumListController.swift index 55124681e..563437034 100644 --- a/Yep/AlbumListController.swift +++ b/Yep/AlbumListController.swift @@ -11,7 +11,7 @@ import Photos // @note use this model to store the album's 'result, 'count, 'name, 'startDate to avoid request and reserve too much times -class Album: NSObject { +final class Album: NSObject { var results: PHFetchResult? var count = 0 var name: String? @@ -21,7 +21,7 @@ class Album: NSObject { private let defaultAlbumIdentifier = "com.Yep.photoPicker" -class AlbumListController: UITableViewController { +final class AlbumListController: UITableViewController { var pickedImageSet = Set() var pickedImages = [PHAsset]() @@ -32,6 +32,7 @@ class AlbumListController: UITableViewController { let albumlistCellIdentifier = "AlbumListCell" var assetsCollection: [Album]? + lazy var pickPhotosVC: PickPhotosViewController = { let vc = UIStoryboard(name: "PickPhotos", bundle: nil).instantiateViewControllerWithIdentifier("PickPhotosViewController") as! PickPhotosViewController @@ -124,9 +125,11 @@ class AlbumListController: UITableViewController { for (_, result) in results.enumerate() { result.enumerateObjectsUsingBlock { (collection, idx, stop) in - if let album = collection as? PHAssetCollection { - let assetResults = PHAsset.fetchAssetsInAssetCollection(album, options: options) + if let album = collection as? PHAssetCollection{ + guard album.localizedTitle != NSLocalizedString("Recently Deleted", comment: "") else { return } + let assetResults = PHAsset.fetchAssetsInAssetCollection(album, options: options) + var count = 0 switch album.assetCollectionType { case .Album: @@ -152,6 +155,7 @@ class AlbumListController: UITableViewController { } } + return list } diff --git a/Yep/AppDelegate.swift b/Yep/AppDelegate.swift index 608502ba9..ef94a5240 100644 --- a/Yep/AppDelegate.swift +++ b/Yep/AppDelegate.swift @@ -16,7 +16,7 @@ import Appsee import CoreSpotlight @UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { +final class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? @@ -40,10 +40,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // 默认将 Realm 放在 App Group 里 let directory: NSURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(YepConfig.appGroupID)! - let realmPath = directory.URLByAppendingPathComponent("db.realm").path! + let realmFileURL = directory.URLByAppendingPathComponent("db.realm") - return Realm.Configuration(path: realmPath, schemaVersion: 31, migrationBlock: { migration, oldSchemaVersion in - }) + var config = Realm.Configuration() + config.fileURL = realmFileURL + config.schemaVersion = 31 + config.migrationBlock = { migration, oldSchemaVersion in + } + + return config } enum RemoteNotificationType: String { @@ -83,6 +88,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { //Fabric.with([Crashlytics.self]) Fabric.with([Appsee.self]) + #if JPUSH /* #if STAGING let apsForProduction = false @@ -92,6 +98,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { JPUSHService.setupWithOption(launchOptions, appKey: "e521aa97cd4cd4eba5b73669", channel: "AppStore", apsForProduction: apsForProduction) */ APService.setupWithOption(launchOptions) + #endif } let _ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker) @@ -99,8 +106,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum) // 全局的外观自定义 - customAppearance() - + customAppearce() + let isLogined = YepUserDefaults.isLogined if isLogined { @@ -116,7 +123,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } else { startShowStory() } - + return true } @@ -151,6 +158,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UIApplication.sharedApplication().applicationIconBadgeNumber = 0 + // dynamic shortcut items + + configureDynamicShortcuts() + + // index searchable items + if YepUserDefaults.isLogined { indexUserSearchableItems() indexFeedSearchableItems() @@ -238,8 +251,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { println("didReceiveRemoteNotification: \(userInfo)") + + #if JPUSH //JPUSHService.handleRemoteNotification(userInfo) APService.handleRemoteNotification(userInfo) + #endif guard YepUserDefaults.isLogined, let type = userInfo["type"] as? String, remoteNotificationType = RemoteNotificationType(rawValue: type) else { completionHandler(UIBackgroundFetchResult.NoData) @@ -260,9 +276,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { syncUnreadMessages() { dispatch_async(dispatch_get_main_queue()) { NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedFeedConversation, object: nil) - } - completionHandler(UIBackgroundFetchResult.NewData) + configureDynamicShortcuts() + + completionHandler(UIBackgroundFetchResult.NewData) + } } case .OfficialMessage: @@ -301,14 +319,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { handleMessageDeletedFromServer(messageID: messageID) + configureDynamicShortcuts() + case .Mentioned: syncUnreadMessagesAndDoFurtherAction({ _ in dispatch_async(dispatch_get_main_queue()) { NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedFeedConversation, object: nil) - } - completionHandler(UIBackgroundFetchResult.NewData) + configureDynamicShortcuts() + + completionHandler(UIBackgroundFetchResult.NewData) + } }) } } @@ -318,11 +340,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate { println(error.description) } + // MARK: Shortcuts + + func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) { + + handleShortcutItem(shortcutItem) + + completionHandler(true) + } + + private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) { + + if let window = window { + tryQuickActionWithShortcutItem(shortcutItem, inWindow: window) + } + } + // MARK: Open URL func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { - if url.absoluteString.contains("/auth/success") { NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.OAuthResult, object: NSNumber(int: 1)) @@ -537,7 +574,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } refreshGroupTypeForAllGroups() - + + if !YepUserDefaults.isSyncedConversations { + syncMyConversations() + } + syncUnreadMessages { syncFriendshipsAndDoFurtherAction { syncGroupsAndDoFurtherAction { @@ -564,10 +605,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func registerThirdPartyPushWithDeciveToken(deviceToken: NSData, pusherID: String) { + #if JPUSH //JPUSHService.registerDeviceToken(deviceToken) //JPUSHService.setTags(Set(["iOS"]), alias: pusherID, callbackSelector:nil, object: nil) APService.registerDeviceToken(deviceToken) APService.setTags(Set(["iOS"]), alias: pusherID, callbackSelector:nil, object: nil) + #endif } func tagsAliasCallback(iResCode: Int, tags: NSSet, alias: NSString) { @@ -689,7 +732,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } - private func customAppearance() { + private func customAppearce() { window?.backgroundColor = UIColor.whiteColor() @@ -712,14 +755,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return shadow }() - let textAttributes = [ + let textAttributes: [String: AnyObject] = [ NSForegroundColorAttributeName: UIColor.yepNavgationBarTitleColor(), NSShadowAttributeName: shadow, NSFontAttributeName: UIFont.navigationBarTitleFont() ] /* - let barButtonTextAttributes = [ + let barButtonTextAttributes: [String: AnyObject] = [ NSForegroundColorAttributeName: UIColor.yepTintColor(), NSFontAttributeName: UIFont.barButtonFont() ] diff --git a/Yep/Base.lproj/Intro.storyboard b/Yep/Base.lproj/Intro.storyboard index fb2e1984c..442f50076 100644 --- a/Yep/Base.lproj/Intro.storyboard +++ b/Yep/Base.lproj/Intro.storyboard @@ -2,7 +2,6 @@ - @@ -375,42 +374,6 @@ - - - - - - - - - @@ -444,10 +398,7 @@ - - - - + @@ -820,8 +771,6 @@ - - diff --git a/Yep/Caches/ImageCache.swift b/Yep/Caches/ImageCache.swift index 6ba42529e..6bbe702f0 100644 --- a/Yep/Caches/ImageCache.swift +++ b/Yep/Caches/ImageCache.swift @@ -11,7 +11,7 @@ import RealmSwift import MapKit import Kingfisher -class ImageCache { +final class ImageCache { static let sharedInstance = ImageCache() diff --git a/Yep/Caches/SendingMessagePool.swift b/Yep/Caches/SendingMessagePool.swift index 01975cdfa..c633a0e5f 100644 --- a/Yep/Caches/SendingMessagePool.swift +++ b/Yep/Caches/SendingMessagePool.swift @@ -8,7 +8,7 @@ import Foundation -class SendingMessagePool { +final class SendingMessagePool { private static var sharedPool = SendingMessagePool() diff --git a/Yep/Configs/YepConfig.swift b/Yep/Configs/YepConfig.swift index 02e897915..3218d1881 100644 --- a/Yep/Configs/YepConfig.swift +++ b/Yep/Configs/YepConfig.swift @@ -20,7 +20,7 @@ let MediaOptionsInfos: KingfisherOptionsInfo = [ .Transition(ImageTransition.Fade(imageFadeTransitionDuration)) ] -class YepConfig { +final class YepConfig { static let appGroupID: String = "group.Catch-Inc.Yep" diff --git a/Yep/Configs/YepHelper.swift b/Yep/Configs/YepHelper.swift index 4f3bde1c0..9be888519 100644 --- a/Yep/Configs/YepHelper.swift +++ b/Yep/Configs/YepHelper.swift @@ -49,9 +49,11 @@ func cancel(cancelableTask: CancelableTask?) { func unregisterThirdPartyPush() { dispatch_async(dispatch_get_main_queue()) { + #if JPUSH //JPUSHService.setAlias(nil, callbackSelector: nil, object: nil) APService.setAlias(nil, callbackSelector: nil, object: nil) UIApplication.sharedApplication().applicationIconBadgeNumber = 0 + #endif } } @@ -89,6 +91,10 @@ func cleanRealmAndCaches() { println("cleaned files!") + // clean shortcuts + + clearDynamicShortcuts() + dispatch_async(dispatch_get_main_queue()) { NSNotificationCenter.defaultCenter().postNotificationName(EditProfileViewController.Notification.Logout, object: nil) } diff --git a/Yep/DataSample.swift b/Yep/DataSample.swift new file mode 100644 index 000000000..543431d71 --- /dev/null +++ b/Yep/DataSample.swift @@ -0,0 +1,336 @@ +// +// DataSample.swift +// Yep +// +// Created by ChaiYixiao on 4/22/16. +// Copyright © 2016 Catch Inc. All rights reserved. +// + +import UIKit +/* 群聊 +Conversation { + type = 1; + updatedUnixTime = 1461292160.994257; + withFriend = (null); + withGroup = Group { + groupID = cc90690d4ce22908b6a7b135a2bf48ed; + groupName = ; + notificationEnabled = 1; + createdUnixTime = 1461292160.992179; + owner = (null); + groupType = 0; + includeMe = 0; + members = RLMArray <0x13844db50> ( + + ); + withFeed = Feed { + feedID = cc90690d4ce22908b6a7b135a2bf48ed; + allowComment = 1; + createdUnixTime = 1461289405.585; + updatedUnixTime = 1461289405.585; + creator = User { + userID = 3500560196378c407c3f26eea4c12055; + username = draveness; + nickname = Draveness; + introduction = ; + avatarURLString = https://s3.cn-north-1.amazonaws.com.cn/yep-avatars/aaf09d9d-748c-493c-99e2-c0f73be375d7-1459149714.jpg; + avatar = (null); + badge = ; + createdUnixTime = 1461207304.891898; + lastSignInUnixTime = 1461291738; + friendState = 0; + friendshipID = ; + isBestfriend = 0; + bestfriendIndex = 0; + longitude = 108.831; + latitude = 34.1297; + notificationEnabled = 1; + blocked = 0; + doNotDisturb = (null); + learningSkills = RLMArray <0x1386bccd0> ( + [0] , + [1] , + [2] , + [3] , + [4] , + [5] + ); + masterSkills = RLMArray <0x13819a460> ( + [0] , + [1] , + [2] , + [3] + ); + socialAccountProviders = RLMArray <0x1384c1720> ( + [0] , + [1] , + [2] + ); + }; + distance = 942.5; + messagesCount = 0; + body = 整理了一些自己写的关于 iOS 开源于 iOS 开源\346\241框架于 iOS 开源\346\241框架\351项目分析的文章,有兴趣的可以关注一下; + kind = github; + socialWork = MessageSocialWork { + type = 0; + githubRepo = SocialWorkGithubRepo { + repoID = 54244176; + name = iOS-Source-Code-Analyze; + fullName = Draveness/iOS-Source-Code-Analyze; + URLString = https://github.com/Draveness/iOS-Source-Code-Analyze; + repoDescription = iOS 源代码分析; + createdUnixTime = 1458356011; + synced = 1; + }; + dribbbleShot = (null); + instagramMedia = (null); + }; + audio = (null); + location = (null); + openGraphInfo = (null); + skill = UserSkill { + category = UserSkillCategory { + skillCategoryID = 90913b93738c8a627129e49db32eeec3; + name = Technology; + localName = Technology; + }; + skillID = 7fd72edb066fd031c2748172d34cbbbb; + name = ios; + localName = iOS; + coverURLString = https://yep-avatars.s3.cn-north-1.amazonaws.com.cn/97b1d79a-fd64-45b0-a2cf-232024846ba4; + }; + group = Group { + groupID = cc90690d4ce22908b6a7b135a2bf48ed; + groupName = ; + notificationEnabled = 1; + createdUnixTime = 1461292160.992179; + owner = (null); + groupType = 0; + includeMe = 0; + members = RLMArray <0x1384c3330> ( + + ); + withFeed = Feed { + feedID = cc90690d4ce22908b6a7b135a2bf48ed; + allowComment = 1; + createdUnixTime = 1461289405.585; + updatedUnixTime = 1461289405.585; + creator = ; + distance = 942.5; + messagesCount = 0; + body = 整理了一些自己写的关于 iOS 开源框架项目分析的文章,有兴趣的可以关注一下; + kind = github; + socialWork = ; + audio = (null); + location = (null); + openGraphInfo = (null); + skill = ; + group = ; + deleted = 0; + attachments = ; + }; + }; + deleted = 0; + attachments = RLMArray <0x1384c0c10> ( + + ); + }; + }; + draft = (null); + unreadMessagesCount = 0; + mentionedMe = 0; + hasUnreadMessages = 0; + lastMentionedMeUnixTime = 1461248960.994258; +} ___conversation +*/ + + +/* 分享的内容 + Optional(Conversation { + type = 1; + updatedUnixTime = 1461140049.017926; + withFriend = (null); + withGroup = Group { + groupID = bd41ec629c7a24933069fc3ef6d613ed; + groupName = ; + notificationEnabled = 1; + createdUnixTime = 1461140049.015901; + owner = (null); + groupType = 0; + includeMe = 0; + members = RLMArray <0x115f8e490> ( + + ); + withFeed = Feed { + feedID = bd41ec629c7a24933069fc3ef6d613ed; + allowComment = 1; + createdUnixTime = 1461053534.864; + updatedUnixTime = 1461129268.836; + creator = User { + userID = cf292b334af37778c2f92612cb4aa4c2; + username = kevinzhow; + nickname = Kevin; + introduction = Founder of Catch; + avatarURLString = https://s3.cn-north-1.amazonaws.com.cn/yep-avatars/a6088cc6-c74d-4e88-9e06-c88fd9857fdc-1455380485.jpg; + avatar = Avatar { + avatarURLString = https://s3.cn-north-1.amazonaws.com.cn/yep-avatars/a6088cc6-c74d-4e88-9e06-c88fd9857fdc-1455380485.jpg; + avatarFileName = F6E57DF4-FA86-4AF7-9F70-60834A63F98E; + roundMini = <89504e47 0d0a1a0a 0000000d 49484452 00000078 00000078 — 11060 total bytes>; + roundNano = <89504e47 0d0a1a0a 0000000d 49484452 00000050 00000050 — 5935 total bytes>; + }; + badge = tech; + createdUnixTime = 1461048518.954341; + lastSignInUnixTime = 1461219387; + friendState = 2; + friendshipID = 904cc6f48e126bf36cf2739007582dd4; + isBestfriend = 0; + bestfriendIndex = 0; + longitude = 116.597; + latitude = 39.9228; + notificationEnabled = 1; + blocked = 0; + doNotDisturb = (null); + learningSkills = RLMArray <0x10af485d0> ( + [0] , + [1] , + [2] , + [3] , + [4] , + [5] , + [6] , + [7] , + [8] + ); + masterSkills = RLMArray <0x115f38b80> ( + [0] , + [1] , + [2] , + [3] , + [4] + ); + socialAccountProviders = RLMArray <0x1191b5210> ( + [0] , + [1] , + [2] + ); + }; + distance = 0; + messagesCount = 10; + body = 特地配色; + kind = image; + socialWork = (null); + audio = (null); + location = (null); + openGraphInfo = (null); + skill = UserSkill { + category = UserSkillCategory { + skillCategoryID = 90913b93738c8a627129e49db32eeec3; + name = Technology; + localName = Technology; + }; + skillID = 7fd72edb066fd031c2748172d34cbbbb; + name = ios; + localName = iOS; + coverURLString = https://yep-avatars.s3.cn-north-1.amazonaws.com.cn/97b1d79a-fd64-45b0-a2cf-232024846ba4; + }; + group = Group { + groupID = bd41ec629c7a24933069fc3ef6d613ed; + groupName = ; + notificationEnabled = 1; + createdUnixTime = 1461140049.015901; + owner = (null); + groupType = 0; + includeMe = 0; + members = RLMArray <0x11851a060> ( + + ); + withFeed = Feed { + feedID = bd41ec629c7a24933069fc3ef6d613ed; + allowComment = 1; + createdUnixTime = 1461053534.864; + updatedUnixTime = 1461129268.836; + creator = ; + distance = 0; + messagesCount = 10; + body = 特地配色; + kind = image; + socialWork = (null); + audio = (null); + location = (null); + openGraphInfo = (null); + skill = ; + group = ; + deleted = 0; + attachments = ; + }; + }; + deleted = 0; + attachments = RLMArray <0x10ac916f0> ( + [0] Attachment { + metadata = {"image_height":1024,"thumbnail_string":"\/9j\/4AAQSkZJRgABAQAAAAAAAAD\/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAAAAAAAAAQAAAAAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAIegAwAEAAAAAQAAALQAAAAA\/+0AOFBob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAAOEJJTQQlAAAAAAAQ1B2M2Y8AsgTpgAmY7PhCfv\/AABEIALQAhwMBIgACEQEDEQH\/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv\/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8\/T19vf4+fr\/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv\/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8\/T19vf4+fr\/2wBDAAICAgICAgQCAgQGBAQEBggGBgYGCAoICAgICAoMCgoKCgoKDAwMDAwMDAwODg4ODg4QEBAQEBISEhISEhISEhL\/2wBDAQMDAwUEBQgEBAgTDQsNExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExP\/3QAEAAn\/2gAMAwEAAhEDEQA\/APzH\/aCuNMbxdf6rpEiyJe2VnMXTozhIA3PrlSfxryu+8XabJpC2t8srtcqjsiYXDIflck56jPGOhzXnuoCRBJFI7NtVVQMTwPTHbpXtvwJ+D8Hxy+J83gi4upLOOKzuJxJEgclrdQsac8KHbCljnGc161WooR12S\/IypwsuU8luNW0CWdpUtZwD281B\/wC06gOq6HsKfYpST\/F54yP\/ACHX6zR\/8EvNCnvYVg8SXT2cuwmfbCpCsoLHyj82VORjPPXvij\/h1vpLNbCLxJcsJbgpIcQjy4Bj96P7x6\/IPTrXn\/2jQ\/mNlTZ+S41bRlHFi7fWc\/0QUn9taUOV08H6zP8A0xX68W\/\/AASv8OXemre2\/im6d3mKqFWAoYskLJvBxk8fKOmcZq1cf8EntJW7MNt4lvHhFuJBJ5UOTNg\/utuemcDf056Uf2hQ\/mK9jK9mj8em1yw\/g0+P8ZJD\/wCzUn9vWw6afB\/31Kf\/AGev2Osf+CSlhc\/Yxc+I72IzbvtBEULCHBAXHIL5GTxjGMVp6b\/wSDtbvTrq4ufEF\/HcRSlIIfLtz50eTiQtuwmRg7Tk84p\/X6H8weykfiu+r2ruXbT7cknJyZT\/AO1Kb\/atuOVsLYfg5\/m9fuvaf8EZvDs+pNbT+LdRS3ESOJxbwEGQglo9u\/PykAbjwc+1aGgf8EW\/DGoWXna74v1HT59xHlC3gl4wOdysB1yMe1NY2i9pEyg92fg1\/bCfw2dqP+2ZP8yasf8ACTXf2f7IILbys52+SpGfXmv39T\/giZ8P1Of+E91Ln\/pyi\/8Ai6d\/w5M+HeM\/8J5qX\/gHD\/8AFVp9apdybH8\/I164H3be1H\/bCP8AqKX+37ztFbD6W8X\/AMTX70Rf8EXvCUlhHdv4t1WKVycwtb25ZF3YXcVcjJXkgZAPGT1rm4f+COeny6ndWj61q0dvFfR20MxjtD5sDJue52h8qqHjb9457c1SxFN9R2PxBh8U6xbN5luYo29VhiB\/9Bq+PHviwdLvH\/bNP\/ia\/cQf8EY\/D7xTOnirU1ZbsQRq1tb\/ADRZAMx+fgAZOOSf5W4v+CLXheaW4j\/4TLU4lhcKjvZ25EgPVlCzZAHvyc\/XCeIpdw8j8Lk8YeIV+5cgfSOP\/wCJp\/8AwmniX\/n7\/wDIcf8A8TX7WW\/\/AARrsLnxxdeGW8ValBp0Fskyak9lCY5JGIzEqCYHKgkls446V2H\/AA5G8L\/9FFu\/\/BbH\/wDH6mVait7fcbKvVW0n95\/\/0Pyjv\/gx4jFgupeIrmG3njIzkHEgXkqGHBYDvj2Ne7\/sB3N1pvxG1PxPGsXkusFlO8kqxFY7p2djGWZdz\/uhhRk+gNfoJ+0brXgtv2e\/EcsCW05gs5DAEKnZIw2o698gt1Ffnj+zLodtD8HNe8USRWck0d9JLGJ2PngWNlJJugQMoc75FBDZAznBIFcvDGcPO6cpVaXLry236X8jpzXB\/U2oOe9tfnbzP0y0H4s\/DJNLN0LqV5LZmfIk3hyyc7QGJf5iegOOwzXoen\/Fj4WtO9rcXVx5pRWhEaF1+ZO7BSAd\/BBwQOtfzt6npni61uT9mgvERUQKUEgU4UcjHHJrH8SSSHWJFdyzIFQsSSSVUA5PrXt1chowdX2cnaLt66v\/ACOVVpNqUt2f0w6Z4gtZUCRBCOOBgj2ruLPXtNs43vdWl+y2sMbPJJg4UKpI6A8Z4zg4r+VOK\/v4D+4nkT\/ddh\/I1r23jDxfZkG01a9ix\/cuJV\/k1cjylP7X4Giqvqf1o2Gr2cqQ3WmTGaGaJJAwJIyygkAkDIHY4GfSvRNL1XcArEn61\/IdafF\/4s2P\/Hn4o1aLH929nH\/s9dbYftK\/tC6eR9j8ba2mP+n2Y\/zY1jLJJPaRft\/I\/sAsNW0jzksZL2JbqRGkEBdfM2r3C53EHHUDFbel65pt\/fXVhbSZFkyK8uV2bmByBhifkxzkDr9a\/kKtP2vf2nbK5W9g8caoZkXasjyK7hfTcyk49q6PTP24f2q9K877D4xuF+0ktNmC1PmE9S+YfmJyck5opZJUjzXknfby27L877+hEql7WP6z5kvIrF9Tj1S4ZULSBIhA+4A4CL8uDntzn3pL5NYsra3nXU7xzO0cW1IIpCpfgO4C8KvVznAFfyqQf8FAP2r4tNGjP4lilsxz5MlhZNH13fd8jHXn6810c3\/BR39q29W2XVNWsLwWTB4BNptqRGy8ArhBggVv\/ZNba6\/r5GfN73M3p2\/U\/q+WVoFihlLSs3ylwOMgck44ANWvpX8r6\/8ABUH9q57+HU7y60m4ngGEd7BQQD\/usorsrT\/grd+1bDjz4tDm\/wB6zkH\/AKDOKzjlGITbbT7eX+ZXOj+m7BpGYRqXPQDNfzbWv\/BYH9pOHAuNG8Pzf9sLlf5XFbLf8Fh\/jddW7Wt\/4W0VlcYJie7ib8GEpI\/Cn\/Zdft+IudH9FsbiVdy5HsfcZqTBr+evRv8AgsX8Q9Lso7GXwTYTLGMBjfXBc\/VnVyfxNa\/\/AA+c8df9CJYf+B0v\/wAaoeV1+34hzo\/\/0fzo+OnxF8M+MtEludH01dNn2IjoqtHuy4ySucEY9OK\/Sv8A4JzeAfBkfwM0rxhqsavqyXl5dx\/MdywmTytzIG+YfuiELKcHOCDmvx++JHxNvvFXhxPD4WMWsU0cm4MzuzhZOT0RRg9EVR0z0Ffsr+zxo9\/8M\/2Y\/wDhYTSWsL2fg6WSAwRkXJLxvcoXlz\/DK7bVUehznpxTwP1PB\/V6E2te7u76Nfj91zoxWJeKm6lWH+St1f5Lza2Px5vPGfxa1r4iyW0ep6taPq+plUjaWZADcT4UBWIAHzDAxVb9pC9s9R\/aA8Z3GnqqW6axdwxqgCrsgkMS4AwOQma+hP2av2hPjvrfxw8HeFr\/AMa6rfWU+oQNeW93MZl8mE+bKp83fwUQ8jBr4l1vU5Nb1u91uY5e9uJbhj6mVy5\/nX0NPm5+WStZdH3+XkctjJK5pdhpxyKcucc11IBoRqlCnpSgd6eua0RLYBTUoBHanAVKK0QNjADUqg1Io4qdQK0RNyELTwtTBakVOKqwEGOaXFWRGKd5YppCK2KMGrflijyxTsGh\/9L8MJZJbjSIogo3SSvtAHXAUD+dfuzq\/wAUJfBf7J3jjWfBesRpdaDDaaLbzWTBWguEeKFsHarBwhGeWHGc9TX4p+AdIOueNPC3h5s\/6XfW8Z78SXG08fQV+2\/7U\/jO7+CH7OA1HwHLBompa74gTzZ7W1SEyKfNd2khdMMxVFDllO7FTmL5qlKilu\/lutyoL3ZTt5ffrdeelvRs+FPgP+0l8YfFWua6PF+rx6pZ6b4b1m9d7i1tXmVkspEiKz+UJVPmSJyrgnp3r4HjTagX0GK+6vDnxo8YeMfg58TrjxOmlXHkaTaWcV9babaWtyz3t\/ApUzQRRsVaON8qc5x7V8M4J+VRk+1ephoJSn7qWy09L+XczYzqamETH7tNjX+I9P6113hfQ5Nc1WHTY+jHLn0UdTXbFCI7XwV4pvLNL61s3kikGVK45H0zmopPCviS3GZ7GdffYf6V9jW1vHawJBCNqIAqgdgOla39nXv9mnVQubdZRCWBHDspYDHXkA11RpLqZuVj4Sezu4D++idPqpFNC19vSIjg7wG+vNfMPxB1C2vfEMkNmiKlv+7+UAZb+I8e\/FaSpKKvcSlfQ4VVJqYLXpXw68L2mvXU02ox74YlxjkZZunT0HNemXHwx8MyZMaSRH\/Zc\/1zW1PDSmuZGUqsYuzPm8LUoUV7nL8KtN\/5ZXMo+oB\/wqhL8KXH\/Hvdg\/7yf4GtPqdTsL6xDuePgUuK9JuPhnrkYPkvFJ+JH8xWNJ4F8TRdbYt\/usp\/rSeGqL7LK9rB9TkNtG010MvhfXoB89pKPopP8qg\/4R\/Wv+fWX\/vg\/wCFS6M+zK513P\/T\/Mj4LaRZp+1DoViS0VtpUxuHMYBKLaW7TswB4yGXPPGetfoZ+2\/8Z\/Enwx+H3gPSfA99Er6j9ouJ\/PtLaRZ40SIKXgkSSMbi5bgfjXwX+z\/faZc\/GzxF4yurq7tbO0sr6YXFnCZZkWaVIFIQYPKSEHngGvrn9t\/43+JvB3xC0Dwb4XnsnsYtFjuJbbUrO3vI5GlkfbuWeOQq2xR0K1OIoVJ4mi3C6s99rpa9PNGfNFu19f0\/r+u3zR4j+Jmo+L\/2VdX1DVtK0iwudR8TWFp52mWMVk1wlra3E7+aIQqNtaRCMKMZPWvkXRv7Q+0vLpyh5AhHPYHg4z3r6i+NvjMeIvgF4C\/4lOm6NJqN\/rGoSxaXB9mhk8s29qkrR7mAY+W4+XAwOnWvk2Esh3oSD0GPWvWwaSTuravT8P0CcVJOMjtLTWL6zZZLu2SXa+4sw3ZO4NjnIHTAGOlfSnw4bTUtYtS1C0CJJbwxBdi7h5SgMQeDiR9zn6181+GLS71jUYdK3kxFg7g8jC8k19e2ertb2EunrDCVlz8xTLLn+6e2K7eSE9Gx4a2Fn7Sktfw+42bhtCaCRrbcGwSiknIOVwDwQf4u\/wCNWLDR7LUraMw3CxTfKHVzuyWcqMADIwBk5z1qudV0S72fabLy2XaC0bHkDA6cA5APvk5z2rL1BdMBSTTXYhgdyOOVIxznoc8njpWypOKtCZ0PGQlPmq0k1a1lp89OpD4jhl0m2VYZFeWaPKdQAxJAycc4xnjNfIMGjarqM8pjjLMkm19xCncc9d2PTv8AjXpHxP8AEU8V5b6XYytG0P71ihIIJ+6AR7Z\/OuS8EXOr3OuxWlvJuE0yyy7gGzsO4k7gfTn170qrqt8sGn6\/12HRlhNHVi0ru9tdLab+er8j6E+H\/hfUNN0LyBAzSrmWfaN2ztyVyMADr0rrQARmr+ias+lTNOkaOJCu5WHBVXV9uPQlQD7V1z+LLG6V5JrFFkdZwXCxtzKu1eNo+769ecivYpzqQtFRuvU8uUMPUTl7Sz7NX69\/Q8\/Zdwx6d6btyMLXpdl\/wg+rzpYyB43ZUVZGPlLlEQYblgAzByTyeRj0qOxtfCF5c39syeVb2NveSLcNL80rAEWwC4AJMhQEAcjJOADXRSxXM+VwZjWwfs48\/tIteT\/4H\/APNGiAqExkVaJycmmcda9aCPPuyqyEcU3Y1WyfSm59q6NBXZ\/\/1Piz9gTwBqvjDUvE1\/pl5dae9utjAZ7SRY5CskjyOnzcMGEYyPTn2PV\/tiftB+KtB\/aE1vwnYJpmp6Vp0dtAtpqNhbXkYYQIz4aRDIuWY52uK9R\/4JwfD3w1rXhBte1mWWK9XxAJ7JY+FlNpbBSrnaw2jzicZU+9fsz4m+BXwe+Jdv5Xj\/wxpurMwwZLi3Rpf+\/gAcfga5MbmHscSlXheKTtp3td+e1vvMIqm5SlTld7Pyfb7nf5n8yf7Tutwas3gSwttOtNJWDwva3DWdirJbxSX80922xXZ2XcsisRuPJ4wMCvmqJBnPYcCvqH9tVtIj\/ae8VaL4ciW307RZoNJs4UJKxw2NvHbqgyScLsI5NeAeHdH\/tfUorMfczlz6KOtfQYVfuovur\/AH6mqVz1f4daL9jsTqUy4e4+7\/uDp+Z5r1VWNZMISFREmFVBgAdsVdjk3NjNdUdypxsjRDHAqK5u4rS3e6nOEjUsx9gM0zzOa81+Jut\/Y9HXTYzh7o4P+4OT+Z4rpT6nPueKatqUur6nNqM3WVy34dh+Ar2D4TaTnz9YkH\/TJP5t\/QV4fEpdwiDJJr648NaaNH0S3sOjKuW\/3jyf1rXDq8rkVnaNjrUI27RTweKz1fFOEp716dOZ58olvdt+Y9agMhJyahMpY5NNLADJNdlORlynlXijx9q2m6xLp9gqKkWASwyScZ9a5v8A4WT4lPeL\/vj\/AOvXPeKLy3vtfubq1bfG7cMO+ABWBnnFebUxVTmdpHoQpRsro+mPgfp3j\/43fEzTvhp4fkhW61ATMrPHlVEMLyknHPRMfU19+f8ADvj9oH\/n+sf+\/T\/41w3\/AASL8FjW\/j\/q\/jKZcx6FpLhW9JbqRUX\/AMcWSv6N9wrwMfnOJpVeSnUaOiGHptXcT\/\/V9N\/YAstG8D\/s0+Eta1+VbWLVrrUJ\/NkcJEpaYqpcsQASsQUfWv1Y0SayvbeO9s5UngbBEkbB0I9Qykg1\/F9p+vfEnwGDb6LqV7psbHmJZHSNvqhOxvyNe0fDf9rj4sfD6Ui2kVk+6xgd7SRh0IJhIQ8eqGuPF5LKrOVaMtyU\/I8x+K3ihvF3xV8S+LX+b+0dVvLgH2kndh+hFT+Ctd0LSkka8cxzSHBJU4CjoMiu9gn\/AGavGIIuv7T8KXbZO7H2u23H1K5cDP8Asitiy\/Zb8Q+LbYXnwq1vS\/EoJP7m2nCzgDpujOSue27Fe9DEQglGWnr\/AFYSlbUmtNUsr1d1nMkg\/wBkg1ppKQwxXhfin4V\/E3wBK3\/CU6Je2Gw8yNE2wf8AbRcr+tc\/YeLtfssCG5Z1HZ\/nH612QlGSvHUp1Ln0\/wCeAOa+b\/GmsHV9dllU5ji\/dp9B3\/E1sH4kahJZSW00C+YykB1JGCRjODn+debjLNWt+iM2d34E0oalr0W8ZSH9434dP1r6XErA4NeQfDux+x6a18\/DTnj\/AHV4H5nNelpOABnrXZRVkc9W7ZsebS+YTWbvGATSm5ULha6oy7nK49jUXL8UqvEDiYbx3XnkfhWT9ryOegryTxzrl7HfpaWkrRqq7jtOMkn29q6HXjBXsTGk5Oxwc8RS6eAcbWK\/kaiZWQ4NQlmclmOSeSaUNivLv1O4\/oV\/4I9eDDp3wl8UePJEw2raolqjesdpEDx7bpm\/Kv1\/+avjL\/gn34N\/4Qr9kbwfaMmybULd9Rl9zdyNKp\/74Kivs35q+OxlTnrSl5nZFWSR\/9b89fGmjeIL3RXtPDaRNLJw\/mHB299uQRk+p6V8lav4Y1nQ226tay2uP4nU7fwdcqa+wvDnx+8Ea9DJJ4+0X+zEjIU32mkhGc9FEDggnHOFxgckivS9JsfCvjqNh4A1q11Q45tpT9nuAPQxyHafwatI4iVLSpH+vUx5mtz82gtzGu6M7l9RyPzFWLbUmt5FmVSkiHKuhIYH1BHI\/Cvsjxj8HdKtroprOmSabcN0ZVMJPuP4W+uDXi2s\/CXUIcvplwlwv92ddjf99r1\/EV2Qrwmro0Uux1fgr9rX47+CEW10rxFNe2q4H2XUwt7ER6YmDOB\/usK9hh\/aX+Bfj8eV8cfhlZtM\/D6hoEhtZs\/3jE5wT\/21xXxJqnhnU9KJ\/tG2lgH94jen\/fS\/1FYyw3QG6BvMX1Q7v06ih4enL3krPutPyE0mfecnwZ\/ZW+JFwp+EXxAOiTyjiy8RIIGD5GFWU7Iz9RIxrJ8YfsJftHeErT+19N0mPxDp55S60iZbhWX1C8OfwBr4dlmklj8uTp7f4V6f8Kfin47+Geri78I+JNQ0NeuLSeSNGb\/aQEow+qkU0q0bKnO\/r\/mrfqTy9jrm8Ra14SlXQfFWlzWMsA2eXKjROMcfckANdFY+LtCvRgTeWx7ONv69K+odK\/bh8f8AiHSxoXxW0rRfHunlSD9tt0hugMYyssSlMjrloTz1qOG0\/YP+J9zt12DV\/h1cyn78cZurVSf9qLzBtz6wR8d6644ypT\/i0\/u1\/wCD+AnFng8dxHKgaNgynuDkfnTjLk19HSf8E9fFfiOyfX\/2a\/HOi+N7RfmEdtcLDcAejIGdQf8AeKfSvmbxv8M\/2gPhDKY\/iR4ZvrOJDgzSQloTj0mj3Rn8666GZUar5Yy17dfuepm4GH4j8Qx6VbmFCTPIp2Adu2TXjTyNIxdySTySe9WtV1VtXvGvMYBAAGc4xWburec+ZiUbImDYq\/p1hcatfwaTZjdNdSJDGB3aRgqj8zWWGr6c\/Y08G\/8ACfftSeB\/DjJ5kX9qRXUq9vLtM3DZ9sR1jUnyRcuxUVd2P63\/AAX4ct\/B3g\/SvCVmAItLs4LRMf3YY1QfyrpvmpO9L+NfFN3dzquf\/9f8XLjxNq\/iK7TTtYghuEi3MEPyCBcDcRIpyMAfMWycjnJq3dpoK2sS+Gp57JbhzmS5+X7QEIACvHyqg9iBk8k5HCWMvhvW9CmhlRdDVGDSzrvljnbjZERkuMcthcgdT2qs\/h++sLN\/E1oYr6Rm2xG1+dIAB\/rHUDKkfwgjrknkc96ttsQex6H8Wfi\/8LtONtqN2b61mby0tLsi6t4wCNzPktj0VSc9SenPoWifGf4X+LRMvjLSH0N4U3Peac+6A5OB+5kzyT0VSvQ18g6ZLNHcl7SdoLa1y9xcpuDSbj0IY4Yk\/KgIGepHWtPUPElvqdtHFf2UcayTM8cNqDHIuQArNxtcnoMjPB6ZrKWGg3e1n3WgrH2nb+EdI8VQm4+Hus2esqf+WO8QXA9jFIQCfZWNeR+KfhraW141t4h017G665KNDJ9e2frzXj728GlXs91pGpLNqqARJu\/dGMsPm+YnYzqPlGD1yeSK7zwj8Z\/i34fePQdZn+36avzSwalGJo1jAyzDeCMAdCOSelJQqR+F39dH\/XyHqcnqfw7u1y2n3CzDslwOf++15rgNR0C\/07P2+2khA\/iA8xPzHIr6htfit8G\/FTH+17C68OyOcLNbnzoCfUxOSR9FcAeldVcfDu9v4Be+Eru21qF13KLdts20+sMm1j\/wHdWixFvjVvX\/ADC66nw\/Al1Cxl0+Q5wRmJucHrx1\/Sta08Wa1ZkJI\/mgdnHP59a9i13wbp7XLwapZm3uFPzZUxyA+44P5iuEv\/BV2uTYziUdlmHP\/fQrpjPqh2HaV49hs72PUYfOsLuM7kuLd2jkU+odCrD8DX2D4P8A+Cif7R\/wzsY4rfxEviixyENnrcYuwV7jzvkuAMeshr4KvtIurHP2yB4f9ofOn5isoQE\/NCQw\/wBk\/wBOtFVRqx5akUxH6uQftd\/sQ\/GwiH9oX4XN4ev5eH1Pw84xk\/xMi+S4\/Hzfxrbg\/Yq\/Zq+Na\/af2XPizY3FzIMppes4iuP93BEUvt\/qm+tfj+6diP6VFsKsGU4I5H\/66540pU\/4M2vxX3MTimfeXxV\/Yf8A2lvhBcKniDw7JewyFhHNpzC6V9o3EhF\/ejA55QV9Q\/8ABJHwNNq37TOoeI72Jk\/4R7SJnw6kFZbl1hUEHkHZ5lfAXwz\/AGxP2lvhIIbfwl4rvHtIfu2l6ReW4BGCFjuA4QEcHZtPvX298K\/+Cps2hX7Xvj\/wjFDeTQ+Q+o6DKbeXbgbS0E29ZNuAQplVRzgDJrSpXqypSpzV35EWlGSaV0f03UfhX5ifDL\/gpT8BfGkapH4utLJ1hTMGvQvYTeYPvkzJvtyD2AbNewf8NyfBL\/odvCf\/AIM1\/wAK+fldOzi\/uZtzrsf\/0Pxs8Q6JrHh7Ut6WMV9ptvvitxGwniG7jczRnmQ9TuxzwBwMZNnLqHg+5+wWFz9i1S4X\/SJQ237PH97y8jneQPmA5HCDnNVvDclzY6u+oaDeTWVnaKrzzg4OOMjbwCXbhFP49Dja8QeKNA8UX7X\/AIk0k6c1zmRJ7TOWUn7zo\/yuT3ZduTXdrszMTTPEEOuaZPD4zhEmn27bzPCoimMzAhAMDa7Hn7w4GTmk0ux0q+ebUPD1811qbBUtoLkBJF427gxJVmVeEAPv2Arc8ZeDL6wNj\/winl32n28Uc6QAhpwzqGZ7iH7wZj7YC4FchAt1oVs3jPVV2Xt07CyQjb838c+3jCp0T1b6UJpq6C5QbQdZS6Tw1NDNak\/vbjz1wFKZzIfRVU+vP41YuPFGqxR4sGkXTI1+zwxsAyNtGfnDcEt1bHTOKXSPEfiaK4j0ewuTfC7AR4JfnQl+qnJ\/EkYx+Faeor8OLzWBZRPcWcMbgOy\/vInP8ZQZ3KCehOeOau\/cZWij8Ny2VrdavBJBPdudsVt8waMEDdtbJG45Ax6cUKmtTanPq+iTNO5B2m2dkeIDhcp1woGMYx70utaRrwupPFFtF59oPlgmtmJjjCjCDK8rtHY4561jR\/adC0UTIWS81PhMZDCEHkjv87cD2FNMD22y+N\/jDSNLtdJ8YRR635xJEd6oJSLO1cMRuQk5PBXAro9O8QfDHxfcfYrc3Gh3xHMUn7+Hpng5Dgfi30r51t9Qv7zUINEvI\/tTEiN1nwWVs\/NtccgD3Jq2ZvDKSXdvpc0kTlTFG8oGwAn5iGXkbhwM1n7KK+HT0C3Y+g9Q8EazFCbnTxHqFv18y1bzBj3X74\/Fa8t1Dw3pd4xMsIR+7J8pB\/CuHsrrxP4YRLnSZZYNpJEsTlo29BgZGa9OufivIbiLS\/FVimoShF8yQcSh2GcBlwxxnuTzVLmXmPU88vPCV2nNnMJB\/dkHP5iuUu9Ou7M\/6VC0fuOV\/OvoCyufCHiIkaHeGCXGTDcDp\/wIDP6fjTLvQ761UtJHuj\/vJhl\/Mf1qlMD5x2g8qc\/SmFK9jvPDWlXvLxBW\/vJ8p\/wrmLvwRdLzYyh\/9l+D+dUFjgCtJsNa95pGp2JxdQMo9QMj8xVHyZf7jfkaAP\/R\/IqDR9D+Inh02fhNo9F\/s4ebcQ3UmIppHOFYTMeXx8qq3bOMc1zc3hvXPD0LeKvG0DMkDCG0jkwVmlxlenBjQfMx\/i4HcmsbV73RZ1Hh3S5mTTLEF9+3D3U3AaQqSMZ\/gUn5UHqTXSeANeu9L0661DxH\/pmg2w2taTfMs07A+XHHnO0\/xMR91R7iuz3krr\/gmfmedTXsssyalHPM+ozSM8jjg5J4wRyWJ59OgFeqeI\/FdlHYWvhbxfZjVb22T\/SLreVmhLHIiRhwdgPzbgRuJFL4cu\/h3e6hNqumK2l6mEb7HBcNvtkmPCtvI3fL\/CG74NcPq+j+MPCDT2GsWrxtfY\/eMCxfOeUcHDbs89fzqrqTswOw8PeGtP1DSr+88BTPc6hsCeXMBG8MT8OQc4Zj90EY4JxzXmbaVrUdwnhyS1KTySjarLhyxwAMkZx\/+uun1W2bQrWHwTDIsVzOyy37k4CtjKRE+kY5Yf3j6itnwZ4l8UW\/iOHw\/NMl1DGzLIZcOIowP3hV+oCrnocU03qwMO+v76G9TQvDV00cGmqRvViokkyPMc+oLHjPAUZq9p2sW\/ieG4vfGMZdLGIYuYvllBzhEH8JJOccDFVNQ07wJrV5Ivhq6exO4hEu\/uMOxDjOM+jD8ad4r0a88OaFZaLGvmJJm4nmTlHkPCqG7hF\/U5pjHaVp+mGK7vdCvPPvJIykMUg2SDfw55OC23OMGuIu7WXT4fstzHJFPu+YMMDaOnHXOafp9g2sapb6dZrsMpVM+n95j+prrtR8X6nd6lKLZEurK3O2OOVAwCJwDnrk4z1qhmZfXuo6HbWmj2ErxyKvnSBT1eTkAj2XHFJaXMOu6mtnqMYZ5Djz0+RuB1I6HGPTNasUWn+Kri51mKX7BcRASSGT5oueOD1GfQg1WtNFvtLs73WJCsv7spG8bBgTIcFuOmB60XAhsoNPjjuW0u6V5jGY4gR5bHJ5OScHjpUen+IvE3hxhFFNLG4P3JPukfjz+VcgOEI45\/OusGo3On+HYIztkM8jsBIAwCLgYGemTQB6JH48sHsbe4163Akn3fNEMEBTjPHXP0Nblld6Lqg3aXdK5P8AC3BH+fpXi93f2GppCl7H9mKJtRosMmM916j86uS6Dqv2+JFdZXBUhkYBgCc5I60WGe1Payx\/fXj1HI\/Oo\/Krym+8Y+INN1ieGCXCKxVVccAD8jSf8LF8Sf34v++T\/jS1Gf\/S\/ITxtoGjXfgGx8dw26Wt5dSbJUg+WI8E5CHODx2IHtWR8QreLT\/Eem+DLUbbCyhtiid2e5RJJXY92YtjPYAAdK6\/xT\/yQ3SP+u4\/9BauX+KH\/JTYf+uOn\/8AoiKuqk7qN\/MxRzPhaztPEPxBtLO\/iUQzXOGjQbVwuTj6HHPetG1+KnjeDXX1B7wzCSXPlSjfEvPAVT90DtjFVvhx\/wAlK0\/\/AK+W\/wDZq4Rf+P0f9dP61o0m9exSPZfiv4Z0uxkttdtVZJtREk0ozld\/BJGeRknnn6YritJH2LwHqeqW\/E808VoW7iJgXYD03FQD7cV6r8Yv+QTpH\/XGT+Qryu0\/5Jpff9hCD\/0W9EfgQdDj3hjXTo5wPmeR1J9lC4\/ma9Fn8S6v4b8K6TY6Y48m5jkmlR1DhzvK4II+6AOgrz+X\/kEQ\/wDXaX\/0FK6LxN\/yL+hf9esn\/o1quSvYaPVoND0lfC1x4rtYFt7uS1Yfu8hV3DkqCTgnpXz4k0tvvjhbAbg\/TkfyJr6Zt\/8AkmMn\/Xt\/SvmKT\/WN9TSh1GdOgEPgxnj4NxdhHPqqLkD8zWBaX95YSebZytGf9k9fqOhroG\/5EqP\/AK\/W\/wDQBXKVSBnsaaFpmuaNb393EEmkQMzx\/KSfp0\/SuL8Y20dld29jDny4YVC5+pz+del6H\/yLdp\/1yFeeePP+Qun\/AFyX+ZqYlM5DarXCLjAJGQK1PETuNduGBwVfAI7AAAVmr\/x8x\/Va0PEX\/Icuv981ZJGmt3u0R3O24UdBKobH49f1p\/8AbH\/Trb\/98f8A16xT1FLQNH\/\/2Q==","image_width":768}; + URLString = https://s3.cn-north-1.amazonaws.com.cn/yep-topic-attachments/a4118fa4-1e39-4280-a06b-51e873de1d6a-1461053534.jpg; + } + ); + }; + }; + draft = Draft { + messageToolbarState = 0; + text = ; + }; + unreadMessagesCount = 0; + mentionedMe = 0; + hasUnreadMessages = 0; + lastMentionedMeUnixTime = 1461096849.017934; + }) ___Toshare + */ + +/* 私聊 + Conversation { + type = 0; + updatedUnixTime = 1461220462.349238; + withFriend = User { + userID = 120a8931c7475c4b0c0bfab6cbc24eb5; + username = litvoid; + nickname = litvoid; + introduction = ; + avatarURLString = https://s3.cn-north-1.amazonaws.com.cn/yep-avatars/05a79472-67fc-4ab2-a740-28754f93623e-1454211286.jpg; + avatar = Avatar { + avatarURLString = https://s3.cn-north-1.amazonaws.com.cn/yep-avatars/05a79472-67fc-4ab2-a740-28754f93623e-1454211286.jpg; + avatarFileName = C3CC9274-C7DF-4C3F-9C58-927874DE5948; + roundMini = <89504e47 0d0a1a0a 0000000d 49484452 00000078 00000078 — 37891 total bytes>; + roundNano = <89504e47 0d0a1a0a 0000000d 49484452 00000050 00000050 — 17606 total bytes>; + }; + badge = ; + createdUnixTime = 1454210821; + lastSignInUnixTime = 1460015788; + friendState = 2; + friendshipID = f77d5cba88222b35efc08999e3d83c35; + isBestfriend = 0; + bestfriendIndex = 0; + longitude = 151.148; + latitude = -33.7518; + notificationEnabled = 1; + blocked = 0; + doNotDisturb = (null); + learningSkills = RLMArray <0x13fb49fa0> ( + + ); + masterSkills = RLMArray <0x13fb49ff0> ( + + ); + socialAccountProviders = RLMArray <0x13fb4a1c0> ( + + ); + }; + withGroup = (null); + draft = Draft { + messageToolbarState = 0; + text = ; + }; + unreadMessagesCount = 0; + mentionedMe = 0; + hasUnreadMessages = 0; + lastMentionedMeUnixTime = 1461177262.349245; + } ___conversation + + */ \ No newline at end of file diff --git a/Yep/Extensions/DeviceGuru+Yep.swift b/Yep/Extensions/DeviceGuru+Yep.swift new file mode 100644 index 000000000..833dd432c --- /dev/null +++ b/Yep/Extensions/DeviceGuru+Yep.swift @@ -0,0 +1,22 @@ +// +// DeviceGuru+Yep.swift +// Yep +// +// Created by NIX on 16/4/29. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import DeviceGuru + +extension Hardware { + + var yep_supportQuickAction: Bool { + + switch self { + case .IPHONE_6S, .IPHONE_6_PLUS: + return true + default: + return false + } + } +} diff --git a/Yep/Extensions/String+Yep.swift b/Yep/Extensions/String+Yep.swift index 61f70f9f6..6edc9114e 100644 --- a/Yep/Extensions/String+Yep.swift +++ b/Yep/Extensions/String+Yep.swift @@ -237,4 +237,122 @@ extension String { return attributedString } + + func yep_keywordSetOfEmphasisTags() -> Set { + + let text = self + let textRange = NSMakeRange(0, (text as NSString).length) + + let keywordExpression = try! NSRegularExpression(pattern: "(.+?)", options: [.CaseInsensitive]) + + let matches = keywordExpression.matchesInString(self, options: [], range: textRange) + let keywords: [String] = matches.map({ + let matchRange = $0.rangeAtIndex(1) + let keyword = (text as NSString).substringWithRange(matchRange) + return keyword.lowercaseString + }) + + let keywordSet = Set(keywords) + return keywordSet + } + + func yep_highlightWithKeywordSet(keywordSet: Set, color: UIColor, baseFont: UIFont, baseColor: UIColor) -> NSAttributedString? { + + let text = self + let textRange = NSMakeRange(0, (self as NSString).length) + + let attributedString = NSMutableAttributedString(string: text) + + attributedString.addAttribute(NSForegroundColorAttributeName, value: baseColor, range: textRange) + attributedString.addAttribute(NSFontAttributeName, value: baseFont, range: textRange) + + let highlightTextAttributes: [String: AnyObject] = [ + NSForegroundColorAttributeName: color, + ] + + keywordSet.forEach({ + if let highlightExpression = try? NSRegularExpression(pattern: $0, options: [.CaseInsensitive]) { + + highlightExpression.enumerateMatchesInString(text, options: NSMatchingOptions(), range: textRange, usingBlock: { result, flags, stop in + + if let result = result { + attributedString.addAttributes(highlightTextAttributes, range: result.range ) + } + }) + } + }) + + return attributedString + } + + /* + func yep_highlightEmphasisTagWithColor(color: UIColor, baseFont: UIFont, baseColor: UIColor) -> NSAttributedString? { + + let text = self + let textRange = NSMakeRange(0, (text as NSString).length) + + let keywordExpression = try! NSRegularExpression(pattern: "(.+?)", options: [.CaseInsensitive]) + + let matches = keywordExpression.matchesInString(self, options: [], range: textRange) + let keywords: [String] = matches.map({ + let matchRange = $0.rangeAtIndex(1) + let keyword = (text as NSString).substringWithRange(matchRange) + return keyword.lowercaseString + }) + + guard !keywords.isEmpty else { + return nil + } + + let keywordSet = Set(keywords) + + println("EmphasisTag keywords: \(keywords)") + println("EmphasisTag keywordSet: \(keywordSet)") + + guard !keywordSet.isEmpty else { + return nil + } + + let emphasisTagExpression = try! NSRegularExpression(pattern: "", options: [.CaseInsensitive]) + let encodedString = emphasisTagExpression.stringByReplacingMatchesInString(text, options: [], range: textRange, withTemplate: "") + + println("EmphasisTag encodedString: \(encodedString)") + + let encodedData = encodedString.dataUsingEncoding(NSUTF8StringEncoding)! + let attributedOptions: [String: AnyObject] = [ + NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, + NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding + ] + guard let decodedString = try? NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil).string else { + return nil + } + + println("EmphasisTag decodedString: \(decodedString)") + + let decodedStringRange = NSMakeRange(0, (decodedString as NSString).length) + + let attributedString = NSMutableAttributedString(string: decodedString) + + attributedString.addAttribute(NSForegroundColorAttributeName, value: baseColor, range: decodedStringRange) + attributedString.addAttribute(NSFontAttributeName, value: baseFont, range: decodedStringRange) + + let highlightTextAttributes: [String: AnyObject] = [ + NSForegroundColorAttributeName: color, + ] + + keywordSet.forEach({ + if let highlightExpression = try? NSRegularExpression(pattern: $0, options: [.CaseInsensitive]) { + + highlightExpression.enumerateMatchesInString(decodedString, options: NSMatchingOptions(), range: decodedStringRange, usingBlock: { result, flags, stop in + + if let result = result { + attributedString.addAttributes(highlightTextAttributes, range: result.range ) + } + }) + } + }) + + return attributedString + } + */ } diff --git a/Yep/Extensions/UIImage+Yep.swift b/Yep/Extensions/UIImage+Yep.swift index f1881b4cd..d13f742a0 100644 --- a/Yep/Extensions/UIImage+Yep.swift +++ b/Yep/Extensions/UIImage+Yep.swift @@ -398,10 +398,12 @@ extension UIImage { CGContextDrawImage(bitmapContext, drawTransposed ? transposedRect : newRect, CGImage) - let newCGImage = CGBitmapContextCreateImage(bitmapContext)! - let newImage = UIImage(CGImage: newCGImage) + if let newCGImage = CGBitmapContextCreateImage(bitmapContext) { + let newImage = UIImage(CGImage: newCGImage) + return newImage + } - return newImage + return nil } func transformForOrientationWithSize(size: CGSize) -> CGAffineTransform { diff --git a/Yep/Helpers/KVO/ObjectKeypathObserver.swift b/Yep/Helpers/KVO/ObjectKeypathObserver.swift index b8a0538b2..c51e472b2 100644 --- a/Yep/Helpers/KVO/ObjectKeypathObserver.swift +++ b/Yep/Helpers/KVO/ObjectKeypathObserver.swift @@ -8,7 +8,7 @@ import UIKit -class ObjectKeypathObserver: NSObject { +final class ObjectKeypathObserver: NSObject { weak var object: NSObject? var keypath: String diff --git a/Yep/Helpers/YepAlert.swift b/Yep/Helpers/YepAlert.swift index ea193e1da..2794819ac 100644 --- a/Yep/Helpers/YepAlert.swift +++ b/Yep/Helpers/YepAlert.swift @@ -9,7 +9,7 @@ import UIKit import Proposer -class YepAlert { +final class YepAlert { class func alert(title title: String, message: String?, dismissTitle: String, inViewController viewController: UIViewController?, withDismissAction dismissAction: (() -> Void)?) { diff --git a/Yep/Helpers/YepHUD.swift b/Yep/Helpers/YepHUD.swift index cc650ff42..edfdf4d85 100644 --- a/Yep/Helpers/YepHUD.swift +++ b/Yep/Helpers/YepHUD.swift @@ -8,7 +8,7 @@ import UIKit -class YepHUD: NSObject { +final class YepHUD: NSObject { static let sharedInstance = YepHUD() diff --git a/Yep/Helpers/YepSoundEffect.swift b/Yep/Helpers/YepSoundEffect.swift index 82abf8080..35ae61c93 100644 --- a/Yep/Helpers/YepSoundEffect.swift +++ b/Yep/Helpers/YepSoundEffect.swift @@ -8,7 +8,7 @@ import AudioToolbox.AudioServices -class YepSoundEffect: NSObject { +final class YepSoundEffect: NSObject { var soundID: SystemSoundID? diff --git a/Yep/Helpers/YepUserDefaults.swift b/Yep/Helpers/YepUserDefaults.swift index 98e8a05ad..76e9c7400 100644 --- a/Yep/Helpers/YepUserDefaults.swift +++ b/Yep/Helpers/YepUserDefaults.swift @@ -29,6 +29,8 @@ private let longitudeShiftKey = "longitudeShift" private let userLocationNameKey = "userLocationName" +private let syncedConversationsKey = "syncedConversations" + struct Listener: Hashable { let name: String @@ -44,7 +46,7 @@ func ==(lhs: Listener, rhs: Listener) -> Bool { return lhs.name == rhs.name } -class Listenable { +final class Listenable { var value: T { didSet { setterAction(value) @@ -104,6 +106,15 @@ class YepUserDefaults { } } + static var isSyncedConversations: Bool { + + if let syncedConversations = YepUserDefaults.syncedConversations.value { + return syncedConversations + } else { + return false + } + } + // MARK: ReLogin class func cleanAllUserDefaults() { @@ -122,6 +133,7 @@ class YepUserDefaults { latitudeShift.removeAllListeners() longitudeShift.removeAllListeners() userLocationName.removeAllListeners() + syncedConversations.removeAllListeners() // reset suite @@ -356,6 +368,14 @@ class YepUserDefaults { defaults.setObject(userLocationName, forKey: userLocationNameKey) } }() + + static var syncedConversations: Listenable = { + let syncedConversations = defaults.boolForKey(syncedConversationsKey) + + return Listenable(syncedConversations) { syncedConversations in + defaults.setObject(syncedConversations, forKey: syncedConversationsKey) + } + }() } diff --git a/Yep/Operations/UploadAttachmentOperation.swift b/Yep/Operations/UploadAttachmentOperation.swift index 80ded4afc..ff557b3e7 100644 --- a/Yep/Operations/UploadAttachmentOperation.swift +++ b/Yep/Operations/UploadAttachmentOperation.swift @@ -8,7 +8,7 @@ import Foundation -class UploadAttachmentOperation: ConcurrentOperation { +final class UploadAttachmentOperation: ConcurrentOperation { private let uploadAttachment: UploadAttachment diff --git a/Yep/Protocols/SearchActionRepresentation.swift b/Yep/Protocols/SearchActionRepresentation.swift new file mode 100644 index 000000000..51d328198 --- /dev/null +++ b/Yep/Protocols/SearchActionRepresentation.swift @@ -0,0 +1,48 @@ +// +// SearchActionRepresentation.swift +// Yep +// +// Created by NIX on 16/4/22. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import UIKit + +protocol SearchActionRepresentation: class { + + var searchBar: UISearchBar! { get } + var searchBarTopConstraint: NSLayoutConstraint! { get } + + var originalNavigationControllerDelegate: UINavigationControllerDelegate? { get } + var searchTransition: SearchTransition? { get set } +} + +extension SearchActionRepresentation where Self: UIViewController { + + func recoverSearchTransition() { + if let delegate = searchTransition { + navigationController?.delegate = delegate + } + } + + func prepareOriginalNavigationControllerDelegate() { + // 记录原始的 searchTransition 以便 pop 后恢复 + searchTransition = navigationController?.delegate as? SearchTransition + + println("originalNavigationControllerDelegate: \(originalNavigationControllerDelegate)") + navigationController?.delegate = originalNavigationControllerDelegate + } +} + +extension SearchConversationsViewController: SearchActionRepresentation { + +} + +extension SearchContactsViewController: SearchActionRepresentation { + +} + +extension SearchFeedsViewController: SearchActionRepresentation { + +} + diff --git a/Yep/Protocols/SearchTriggerRepresentation.swift b/Yep/Protocols/SearchTriggerRepresentation.swift new file mode 100644 index 000000000..fc0728359 --- /dev/null +++ b/Yep/Protocols/SearchTriggerRepresentation.swift @@ -0,0 +1,44 @@ +// +// SearchTriggerRepresentation.swift +// Yep +// +// Created by NIX on 16/4/22. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import UIKit + +protocol SearchTriggerRepresentation: class { + + var originalNavigationControllerDelegate: UINavigationControllerDelegate? { get set } + var searchTransition: SearchTransition { get } +} + +extension SearchTriggerRepresentation where Self: UIViewController { + + func prepareSearchTransition() { + // 在自定义 push 之前,记录原始的 NavigationControllerDelegate 以便 pop 后恢复 + originalNavigationControllerDelegate = navigationController?.delegate + + navigationController?.delegate = searchTransition + } + + func recoverOriginalNavigationDelegate() { + if let originalNavigationControllerDelegate = originalNavigationControllerDelegate { + navigationController?.delegate = originalNavigationControllerDelegate + } + } +} + +extension ConversationsViewController: SearchTriggerRepresentation { + +} + +extension ContactsViewController: SearchTriggerRepresentation { + +} + +extension FeedsViewController: SearchTriggerRepresentation { + +} + diff --git a/Yep/Realm/Models.swift b/Yep/Realm/Models.swift index fa70e614d..8fcc08134 100644 --- a/Yep/Realm/Models.swift +++ b/Yep/Realm/Models.swift @@ -237,17 +237,6 @@ class User: Object { } } -func ==(lhs: User, rhs: User) -> Bool { - return lhs.hashValue == rhs.hashValue -} - -extension User: Hashable { - - override var hashValue: Int { - return userID.hashValue - } -} - // MARK: Group // Group 类型,注意:上线后若要调整,只能增加新状态 @@ -785,11 +774,22 @@ class Conversation: Object { var mentionInitUsers: [UsernamePrefixMatchedUser] { - let userSet = Set(messages.flatMap({ $0.fromFriend }).filter({ !$0.username.isEmpty && !$0.isMe }) ?? []) + let users = messages.flatMap({ $0.fromFriend }).filter({ !$0.username.isEmpty && !$0.isMe }) - let users = Array(userSet).sort({ $0.lastSignInUnixTime > $1.lastSignInUnixTime }).map({ UsernamePrefixMatchedUser(userID: $0.userID, username: $0.username, nickname: $0.nickname, avatarURLString: $0.avatarURLString) }) + let usernamePrefixMatchedUser = users.map({ + UsernamePrefixMatchedUser( + userID: $0.userID, + username: $0.username, + nickname: $0.nickname, + avatarURLString: $0.avatarURLString, + lastSignInUnixTime: $0.lastSignInUnixTime) + }) + + let uniqueSortedUsers = Array(Set(usernamePrefixMatchedUser)).sort({ + $0.lastSignInUnixTime > $1.lastSignInUnixTime + }) - return users + return uniqueSortedUsers } dynamic var type: Int = ConversationType.OneToOne.rawValue @@ -1136,6 +1136,76 @@ func userWithAvatarURLString(avatarURLString: String, inRealm realm: Realm) -> U return realm.objects(User).filter(predicate).first } +func conversationWithDiscoveredUser(discoveredUser: DiscoveredUser, inRealm realm: Realm) -> Conversation? { + + var stranger = userWithUserID(discoveredUser.id, inRealm: realm) + + if stranger == nil { + let newUser = User() + + newUser.userID = discoveredUser.id + + newUser.friendState = UserFriendState.Stranger.rawValue + + realm.add(newUser) + + stranger = newUser + } + + guard let user = stranger else { + return nil + } + + // 更新用户信息 + + user.lastSignInUnixTime = discoveredUser.lastSignInUnixTime + + user.username = discoveredUser.username ?? "" + + user.nickname = discoveredUser.nickname + + if let introduction = discoveredUser.introduction { + user.introduction = introduction + } + + user.avatarURLString = discoveredUser.avatarURLString + + user.longitude = discoveredUser.longitude + + user.latitude = discoveredUser.latitude + + if let badge = discoveredUser.badge { + user.badge = badge + } + + // 更新技能 + + user.learningSkills.removeAll() + let learningUserSkills = userSkillsFromSkills(discoveredUser.learningSkills, inRealm: realm) + user.learningSkills.appendContentsOf(learningUserSkills) + + user.masterSkills.removeAll() + let masterUserSkills = userSkillsFromSkills(discoveredUser.masterSkills, inRealm: realm) + user.masterSkills.appendContentsOf(masterUserSkills) + + // 更新 Social Account Provider + + user.socialAccountProviders.removeAll() + let socialAccountProviders = userSocialAccountProvidersFromSocialAccountProviders(discoveredUser.socialAccountProviders) + user.socialAccountProviders.appendContentsOf(socialAccountProviders) + + if user.conversation == nil { + let newConversation = Conversation() + + newConversation.type = ConversationType.OneToOne.rawValue + newConversation.withFriend = user + + realm.add(newConversation) + } + + return user.conversation +} + func groupWithGroupID(groupID: String, inRealm realm: Realm) -> Group? { let predicate = NSPredicate(format: "groupID = %@", groupID) return realm.objects(Group).filter(predicate).first @@ -1248,6 +1318,16 @@ func countOfUnreadMessagesInConversation(conversation: Conversation) -> Int { }).count } +func latestValidMessageInRealm(realm: Realm) -> Message? { + + let latestGroupMessage = latestValidMessageInRealm(realm, withConversationType: .Group) + let latestOneToOneMessage = latestValidMessageInRealm(realm, withConversationType: .OneToOne) + + let latestMessage: Message? = [latestGroupMessage, latestOneToOneMessage].flatMap({ $0 }).sort({ $0.createdUnixTime > $1.createdUnixTime }).first + + return latestMessage +} + func latestValidMessageInRealm(realm: Realm, withConversationType conversationType: ConversationType) -> Message? { switch conversationType { diff --git a/Yep/Services/FayeService.swift b/Yep/Services/FayeService.swift index 1027b9b95..47fe9dd44 100644 --- a/Yep/Services/FayeService.swift +++ b/Yep/Services/FayeService.swift @@ -25,7 +25,7 @@ protocol FayeServiceDelegate: class { let fayeQueue = dispatch_queue_create("com.Yep.fayeQueue", DISPATCH_QUEUE_SERIAL) -class FayeService: NSObject, MZFayeClientDelegate { +final class FayeService: NSObject, MZFayeClientDelegate { static let sharedManager = FayeService() diff --git a/Yep/Services/YepAudioService.swift b/Yep/Services/YepAudioService.swift index e9bac9ae4..b860a8904 100644 --- a/Yep/Services/YepAudioService.swift +++ b/Yep/Services/YepAudioService.swift @@ -21,7 +21,7 @@ extension AVPlayer { } } -class YepAudioService: NSObject { +final class YepAudioService: NSObject { static let sharedManager = YepAudioService() diff --git a/Yep/Services/YepDownloader.swift b/Yep/Services/YepDownloader.swift index ff2c65ffe..309b34092 100644 --- a/Yep/Services/YepDownloader.swift +++ b/Yep/Services/YepDownloader.swift @@ -10,7 +10,7 @@ import Foundation import RealmSwift import ImageIO -class YepDownloader: NSObject { +final class YepDownloader: NSObject { static let sharedDownloader = YepDownloader() diff --git a/Yep/Services/YepLocationService.swift b/Yep/Services/YepLocationService.swift index 1cf91da73..03671c465 100644 --- a/Yep/Services/YepLocationService.swift +++ b/Yep/Services/YepLocationService.swift @@ -8,7 +8,7 @@ import CoreLocation -class YepLocationService: NSObject, CLLocationManagerDelegate { +final class YepLocationService: NSObject, CLLocationManagerDelegate { class func turnOn() { if (CLLocationManager.locationServicesEnabled()){ diff --git a/Yep/Services/YepNetworking.swift b/Yep/Services/YepNetworking.swift index 4ae27cef4..3fbffd9fb 100644 --- a/Yep/Services/YepNetworking.swift +++ b/Yep/Services/YepNetworking.swift @@ -190,7 +190,7 @@ public func apiRequest(modifyRequest: NSMutableURLRequest -> (), baseURL: NSU } #if DEBUG - //println(request.cURLCommandLineWithSession(session)) + println(request.cURLCommandLineWithSession(session)) #endif let _failure: FailureHandler diff --git a/Yep/Services/YepService.swift b/Yep/Services/YepService.swift index b94a2258c..30ebf4202 100644 --- a/Yep/Services/YepService.swift +++ b/Yep/Services/YepService.swift @@ -59,7 +59,8 @@ func saveTokenAndUserInfoOfLoginUser(loginUser: LoginUser) { // MARK: - Register func validateMobile(mobile: String, withAreaCode areaCode: String, failureHandler: FailureHandler?, completion: ((Bool, String)) -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "mobile": mobile, "phone_code": areaCode, ] @@ -447,7 +448,7 @@ enum VerifyCodeMethod: String { func sendVerifyCodeOfMobile(mobile: String, withAreaCode areaCode: String, useMethod method: VerifyCodeMethod, failureHandler: FailureHandler?, completion: Bool -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "mobile": mobile, "phone_code": areaCode, "method": method.rawValue @@ -462,6 +463,40 @@ func sendVerifyCodeOfMobile(mobile: String, withAreaCode areaCode: String, useMe apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } +func sendVerifyCodeOfNewMobile(mobile: String, withAreaCode areaCode: String, useMethod method: VerifyCodeMethod, failureHandler: FailureHandler?, completion: () -> Void) { + + let requestParameters: JSONDictionary = [ + "mobile": mobile, + "phone_code": areaCode, + "method": method.rawValue, + ] + + let parse: JSONDictionary -> Void? = { data in + return + } + + let resource = authJsonResource(path: "/v1/user/send_update_mobile_code", method: .POST, requestParameters: requestParameters, parse: parse) + + apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) +} + +func comfirmNewMobile(mobile: String, withAreaCode areaCode: String, verifyCode: String, failureHandler: FailureHandler?, completion: () -> Void) { + + let requestParameters: JSONDictionary = [ + "mobile": mobile, + "phone_code": areaCode, + "token": verifyCode, + ] + + let parse: JSONDictionary -> Void? = { data in + return + } + + let resource = authJsonResource(path: "/v1/user/update_mobile", method: .PATCH, requestParameters: requestParameters, parse: parse) + + apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) +} + func loginByMobile(mobile: String, withAreaCode areaCode: String, verifyCode: String, failureHandler: FailureHandler?, completion: LoginUser -> Void) { println("User login type is \(YepConfig.clientType())") @@ -555,7 +590,8 @@ func enableNotificationFromCircleWithCircleID(circleID: String, failureHandler: } private func headBlockedUsers(failureHandler failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "page": 1, "per_page": 100, ] @@ -570,7 +606,8 @@ private func headBlockedUsers(failureHandler failureHandler: FailureHandler?, co } private func moreBlockedUsers(inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "page": page, "per_page": perPage, ] @@ -651,7 +688,7 @@ func blockedUsersByMe(failureHandler failureHandler: FailureHandler?, completion func blockUserWithUserID(userID: String, failureHandler: FailureHandler?, completion: Bool -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "user_id": userID ] @@ -710,11 +747,32 @@ func settingsForGroup(groupID groupID: String, failureHandler: FailureHandler?, apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } +// MARK: - Conversations + +func myConversations(maxMessageID maxMessageID: String?, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { + + var requestParameters: JSONDictionary = [ + "per_page": 30, + ] + + if let maxMessageID = maxMessageID { + requestParameters["max_id"] = maxMessageID + } + + let parse: JSONDictionary -> JSONDictionary? = { data in + return data + } + + let resource = authJsonResource(path: "/v1/conversations", method: .GET, requestParameters: requestParameters, parse: parse) + + apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) +} + // MARK: - Contacts func searchUsersByMobile(mobile: String, failureHandler: FailureHandler?, completion: [JSONDictionary] -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "q": mobile ] @@ -899,7 +957,7 @@ struct FriendRequest { func sendFriendRequestToUser(user: User, failureHandler: FailureHandler?, completion: FriendRequest.State -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "friend_id": user.userID, ] @@ -920,7 +978,7 @@ func sendFriendRequestToUser(user: User, failureHandler: FailureHandler?, comple func stateOfFriendRequestWithUser(user: User, failureHandler: FailureHandler?, completion: (isFriend: Bool,receivedFriendRequestSate: FriendRequest.State, receivedFriendRequestID: String, sentFriendRequestState: FriendRequest.State) -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "user_id": user.userID, ] @@ -977,7 +1035,7 @@ func stateOfFriendRequestWithUser(user: User, failureHandler: FailureHandler?, c func acceptFriendRequestWithID(friendRequestID: String, failureHandler: FailureHandler?, completion: Bool -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "id": friendRequestID, ] @@ -1002,7 +1060,7 @@ func acceptFriendRequestWithID(friendRequestID: String, failureHandler: FailureH func rejectFriendRequestWithID(friendRequestID: String, failureHandler: FailureHandler?, completion: Bool -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "id": friendRequestID, ] @@ -1028,7 +1086,8 @@ func rejectFriendRequestWithID(friendRequestID: String, failureHandler: FailureH // MARK: - Friendships private func headFriendships(failureHandler failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "page": 1, "per_page": 100, ] @@ -1043,7 +1102,8 @@ private func headFriendships(failureHandler failureHandler: FailureHandler?, com } private func moreFriendships(inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "page": page, "per_page": perPage, ] @@ -1218,7 +1278,7 @@ let parseDiscoveredUsers: JSONDictionary -> [DiscoveredUser]? = { data in func discoverUsers(masterSkillIDs masterSkillIDs: [String], learningSkillIDs: [String], discoveredUserSortStyle: DiscoveredUserSortStyle, inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: [DiscoveredUser] -> Void) { - let requestParameters: [String: AnyObject] = [ + let requestParameters: JSONDictionary = [ "master_skills": masterSkillIDs, "learning_skills": learningSkillIDs, "sort": discoveredUserSortStyle.rawValue, @@ -1253,7 +1313,7 @@ func discoverUsers(masterSkillIDs masterSkillIDs: [String], learningSkillIDs: [S func discoverUsersWithSkill(skillID: String, ofSkillSet skillSet: SkillSet, inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: [DiscoveredUser] -> Void) { - let requestParameters: [String: AnyObject] = [ + let requestParameters: JSONDictionary = [ "page": page, "per_page": perPage, ] @@ -1267,7 +1327,7 @@ func discoverUsersWithSkill(skillID: String, ofSkillSet skillSet: SkillSet, inPa func searchUsersByQ(q: String, failureHandler: FailureHandler?, completion: [DiscoveredUser] -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "q": q ] @@ -1401,9 +1461,10 @@ func meIsMemberOfGroup(groupID groupID: String, failureHandler: FailureHandler?, } func headGroups(failureHandler failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "page": 1, - "per_page": 50, + "per_page": 100, ] let parse: JSONDictionary -> JSONDictionary? = { data in @@ -1416,7 +1477,8 @@ func headGroups(failureHandler failureHandler: FailureHandler?, completion: JSON } func moreGroups(inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + + let requestParameters: JSONDictionary = [ "page": page, "per_page": perPage, ] @@ -1743,10 +1805,7 @@ func unreadMessages(failureHandler failureHandler: FailureHandler?, completion: let _latestMessage = realm.objects(Message).sorted("createdUnixTime", ascending: false).first - let latestGroupMessage = latestValidMessageInRealm(realm, withConversationType: .Group) - let latestOneToOneMessage = latestValidMessageInRealm(realm, withConversationType: .OneToOne) - - let latestMessage: Message? = [latestGroupMessage, latestOneToOneMessage].flatMap({ $0 }).sort({ $0.createdUnixTime > $1.createdUnixTime }).first + let latestMessage = latestValidMessageInRealm(realm) println("_latestMessage: \(_latestMessage?.messageID), \(_latestMessage?.createdUnixTime)") println("+latestMessage: \(latestMessage?.messageID), \(latestMessage?.createdUnixTime)") @@ -1757,7 +1816,7 @@ func unreadMessages(failureHandler failureHandler: FailureHandler?, completion: private func headUnreadMessagesAfterMessageWithID(messageID: String?, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - var parameters: [String: AnyObject] = [ + var parameters: JSONDictionary = [ "page": 1, "per_page": 30, ] @@ -1777,7 +1836,7 @@ private func headUnreadMessagesAfterMessageWithID(messageID: String?, failureHan private func moreUnreadMessagesAfterMessageWithID(messageID: String?, inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - var parameters: [String: AnyObject] = [ + var parameters: JSONDictionary = [ "page": page, "per_page": perPage, ] @@ -1945,7 +2004,7 @@ enum TimeDirection { func messagesFromRecipient(recipient: Recipient, withTimeDirection timeDirection: TimeDirection, failureHandler: FailureHandler?, completion: (messageIDs: [String]) -> Void) { - var requestParameters = [ + var requestParameters: JSONDictionary = [ "recipient_type": recipient.type.nameForServer, "recipient_id": recipient.ID, ] @@ -2451,7 +2510,7 @@ func batchMarkAsReadOfMessagesToRecipient(recipient: Recipient, beforeMessage: M return } - let requestParameters = [ + let requestParameters: JSONDictionary = [ "max_id": beforeMessage.messageID ] @@ -2477,7 +2536,7 @@ func deleteMessageFromServer(messageID messageID: String, failureHandler: Failur func refreshAttachmentWithID(attachmentID: String, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "ids": [attachmentID], ] @@ -2618,6 +2677,7 @@ struct DiscoveredFeed: Hashable { let creator: DiscoveredUser let body: String + let highlightedKeywordsBody: String? struct GithubRepo { let ID: Int @@ -2817,6 +2877,8 @@ struct DiscoveredFeed: Hashable { return nil } + let highlightedKeywordsBody = feedInfo["highlight"] as? String + var groupInfo = groupInfo if groupInfo == nil { @@ -2893,7 +2955,7 @@ struct DiscoveredFeed: Hashable { skill = Skill.fromJSONDictionary(skillInfo) } - return DiscoveredFeed(id: id, allowComment: allowComment, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: body, attachment: attachment, distance: distance, skill: skill, groupID: groupID, messagesCount: messagesCount, uploadingErrorMessage: nil) + return DiscoveredFeed(id: id, allowComment: allowComment, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: body, highlightedKeywordsBody: highlightedKeywordsBody, attachment: attachment, distance: distance, skill: skill, groupID: groupID, messagesCount: messagesCount, uploadingErrorMessage: nil) } } @@ -2946,6 +3008,7 @@ func discoverFeedsWithSortStyle(sortStyle: FeedSortStyle, skill: Skill?, pageInd let _ = try? realm.write { realm.add(offlineJSON, update: true) + println("offline feeds \(sortStyle)") } } } @@ -2959,21 +3022,29 @@ func discoverFeedsWithSortStyle(sortStyle: FeedSortStyle, skill: Skill?, pageInd apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } -func feedsWithKeyword(keyword: String, pageIndex: Int, perPage: Int, failureHandler: FailureHandler?, completion: [DiscoveredFeed] -> Void) { +func feedsWithKeyword(keyword: String, skillID: String?, userID: String?, pageIndex: Int, perPage: Int, failureHandler: FailureHandler?, completion: [DiscoveredFeed] -> Void) { guard !keyword.isEmpty else { completion([]) return } - let requestParameters: JSONDictionary = [ + var requestParameters: JSONDictionary = [ "q": keyword, "page": pageIndex, "per_page": perPage, ] + if let skillID = skillID { + requestParameters["skill_id"] = skillID + } + + if let userID = userID { + requestParameters["user_id"] = userID + } + let parse: JSONDictionary -> [DiscoveredFeed]? = { data in - //println("feedsWithKeyword \(keyword): \(data)") + //println("feedsWithKeyword \(requestParameters): \(data)") return parseFeeds(data) } @@ -2982,6 +3053,19 @@ func feedsWithKeyword(keyword: String, pageIndex: Int, perPage: Int, failureHand apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } +func hotWordsOfSearchFeeds(failureHandler failureHandler: FailureHandler?, completion: [String] -> Void) { + + let parse: JSONDictionary -> [String]? = { data in + //println("hotWordsOfSearchFeeds: \(data)") + let hotWords = data["hot_words"] as? [String] + return hotWords + } + + let resource = authJsonResource(path: "/v1/hot_words", method: .GET, requestParameters: [:], parse: parse) + + apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) +} + func feedWithSharedToken(token: String, failureHandler: FailureHandler?, completion: DiscoveredFeed -> Void) { let requestParameters: JSONDictionary = [ @@ -3009,7 +3093,7 @@ func myFeedsAtPageIndex(pageIndex: Int, perPage: Int, failureHandler: FailureHan apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } -func feedsOfUser(userID: String, pageIndex: Int, perPage: Int, failureHandler: FailureHandler?,completion: [DiscoveredFeed] -> Void) { +func feedsOfUser(userID: String, pageIndex: Int, perPage: Int, failureHandler: FailureHandler?, completion: [DiscoveredFeed] -> Void) { let requestParameters: JSONDictionary = [ "page": pageIndex, @@ -3112,21 +3196,96 @@ func deleteFeedWithFeedID(feedID: String, failureHandler: FailureHandler?, compl apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } -func creatorsOfBlockedFeeds(failureHandler failureHandler: FailureHandler?, completion: [DiscoveredUser] -> Void) { +private func headCreatorsOfBlockedFeeds(failureHandler failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { - let parse: JSONDictionary -> [DiscoveredUser]? = { data in - if let creatorInfos = data["blocked_topic_creators"] as? [JSONDictionary] { - let creators = creatorInfos.map({ parseDiscoveredUser($0) }).flatMap({ $0 }) - return creators - } - return nil + let requestParameters: JSONDictionary = [ + "page": 1, + "per_page": 30, + ] + + let parse: JSONDictionary -> JSONDictionary? = { data in + return data } - let resource = authJsonResource(path: "/v1/blocked_topic_creators", method: .GET, requestParameters: [:], parse: parse) + let resource = authJsonResource(path: "/v1/blocked_topic_creators", method: .GET, requestParameters: requestParameters, parse: parse) apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) } +private func moreCreatorsOfBlockedFeeds(inPage page: Int, withPerPage perPage: Int, failureHandler: FailureHandler?, completion: JSONDictionary -> Void) { + + let requestParameters: JSONDictionary = [ + "page": page, + "per_page": perPage, + ] + + let parse: JSONDictionary -> JSONDictionary? = { data in + return data + } + + let resource = authJsonResource(path: "/v1/blocked_topic_creators", method: .GET, requestParameters: requestParameters, parse: parse) + + apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion) +} + +func creatorsOfBlockedFeeds(failureHandler failureHandler: FailureHandler?, completion: [DiscoveredUser] -> Void) { + + headCreatorsOfBlockedFeeds(failureHandler: failureHandler) { result in + + guard let page1CreatorInfos = result["blocked_topic_creators"] as? [JSONDictionary] else { + completion([]) + return + } + + let page1Creators = page1CreatorInfos.map({ parseDiscoveredUser($0) }).flatMap({ $0 }) + + guard let count = result["count"] as? Int, currentPage = result["current_page"] as? Int, perPage = result["per_page"] as? Int else { + + println("creatorsOfBlockedFeeds not paging info.") + + completion(page1Creators) + return + } + + if count <= currentPage * perPage { + completion(page1Creators) + + } else { + var creators = [DiscoveredUser]() + + creators += page1Creators + + // We have more creators + + var allGood = true + let downloadGroup = dispatch_group_create() + + for page in 2..<((count / perPage) + ((count % perPage) > 0 ? 2 : 1)) { + dispatch_group_enter(downloadGroup) + + moreCreatorsOfBlockedFeeds(inPage: page, withPerPage: perPage, failureHandler: { (reason, errorMessage) in + allGood = false + failureHandler?(reason: reason, errorMessage: errorMessage) + dispatch_group_leave(downloadGroup) + + }, completion: { result in + if let currentCreatorInfos = result["blocked_topic_creators"] as? [JSONDictionary] { + let currentCreators = currentCreatorInfos.map({ parseDiscoveredUser($0) }).flatMap({ $0 }) + creators += currentCreators + } + dispatch_group_leave(downloadGroup) + }) + } + + dispatch_group_notify(downloadGroup, dispatch_get_main_queue()) { + if allGood { + completion(creators) + } + } + } + } +} + func amIBlockedFeedsFromCreator(userID userID: String, failureHandler: FailureHandler?, completion: Bool -> Void) { let requestParameters: JSONDictionary = [ @@ -3454,7 +3613,7 @@ struct Feedback { func sendFeedback(feedback: Feedback, failureHandler: FailureHandler?, completion: Bool -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "content": feedback.content, "device_info": feedback.deviceInfo, ] @@ -3487,7 +3646,7 @@ func foursquareVenuesNearby(coordinate coordinate: CLLocationCoordinate2D, failu dateFormatter.dateFormat = "yyyyMMdd" let dateString = dateFormatter.stringFromDate(NSDate()) - let requestParameters = [ + let requestParameters: JSONDictionary = [ "client_id": "NFMF2UV2X5BCADG2T5FE3BIORDPEDJA5JZVDWF0XXAZUX2AS", "client_secret": "UOGE0SCBWHV2JFXD5AFAIHOVTUSBQ3ERH4ALHU3WU3BSR4CN", "v": dateString, @@ -3533,20 +3692,32 @@ func foursquareVenuesNearby(coordinate coordinate: CLLocationCoordinate2D, failu // MARK: Mention +func ==(lhs: UsernamePrefixMatchedUser, rhs: UsernamePrefixMatchedUser) -> Bool { + return lhs.hashValue == rhs.hashValue +} + struct UsernamePrefixMatchedUser { let userID: String let username: String let nickname: String let avatarURLString: String? + let lastSignInUnixTime: NSTimeInterval var mentionUsername: String { return "@" + username } } +extension UsernamePrefixMatchedUser: Hashable { + + var hashValue: Int { + return userID.hashValue + } +} + func usersMatchWithUsernamePrefix(usernamePrefix: String, failureHandler: FailureHandler?, completion: ([UsernamePrefixMatchedUser]) -> Void) { - let requestParameters = [ + let requestParameters: JSONDictionary = [ "q": usernamePrefix, ] @@ -3560,14 +3731,13 @@ func usersMatchWithUsernamePrefix(usernamePrefix: String, failureHandler: Failur username = userInfo["username"] as? String, nickname = userInfo["nickname"] as? String, avatarInfo = userInfo["avatar"] as? JSONDictionary - //avatarURLString = avatarInfo["thumb_url"] as? String else { return nil } let avatarURLString = avatarInfo["thumb_url"] as? String - return UsernamePrefixMatchedUser(userID: userID, username: username, nickname: nickname, avatarURLString: avatarURLString) + return UsernamePrefixMatchedUser(userID: userID, username: username, nickname: nickname, avatarURLString: avatarURLString, lastSignInUnixTime: 0) }).flatMap({ $0 }) return users diff --git a/Yep/Services/YepServiceSync.swift b/Yep/Services/YepServiceSync.swift index 6b02d0ddf..029d63b36 100644 --- a/Yep/Services/YepServiceSync.swift +++ b/Yep/Services/YepServiceSync.swift @@ -382,6 +382,80 @@ func syncMyInfoAndDoFurtherAction(furtherAction: () -> Void) { }) } +func syncMyConversations(maxMessageID maxMessageID: String? = nil) { + + myConversations(maxMessageID: maxMessageID, failureHandler: nil) { result in + + guard let realm = try? Realm() else { + return + } + + realm.beginWrite() + + if let userInfos = result["users"] as? [JSONDictionary] { + + let discoveredUsers = userInfos.map({ parseDiscoveredUser($0) }).flatMap({ $0 }) + + discoveredUsers.forEach({ + _ = conversationWithDiscoveredUser($0, inRealm: realm) + }) + + dispatch_async(dispatch_get_main_queue()) { + NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedConversation, object: nil) + } + } + + if let groupInfos = result["circles"] as? [JSONDictionary] { + + groupInfos.forEach({ + syncFeedGroupWithGroupInfo($0, inRealm: realm) + }) + + dispatch_async(dispatch_get_main_queue()) { + NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedFeedConversation, object: nil) + } + } + + var lastMessageID: String? + + if let messageInfos = result["messages"] as? [JSONDictionary] { + + messageInfos.forEach({ + syncMessageWithMessageInfo($0, messageAge: .Old, inRealm: realm) { _ in + } + }) + + let messageIDs: [String] = messageInfos.map({ $0["id"] as? String }).flatMap({ $0 }) + messageIDs.forEach({ + if let message = messageWithMessageID($0, inRealm: realm) { + if let conversation = message.conversation { + conversation.updatedUnixTime = message.createdUnixTime + } + } + }) + + lastMessageID = messageIDs.last + } + + let _ = try? realm.commitWrite() + + dispatch_async(dispatch_get_main_queue()) { + NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedConversation, object: nil) + NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedFeedConversation, object: nil) + } + + if let lastMessageID = lastMessageID { + if let count = result["count"] as? Int, perPage = result["per_page"] as? Int { + if count > perPage { + syncMyConversations(maxMessageID: lastMessageID) + } + } + } + + YepUserDefaults.syncedConversations.value = true + } +} + func syncFriendshipsAndDoFurtherAction(furtherAction: () -> Void) { friendships(failureHandler: nil) { allFriendships in @@ -559,22 +633,7 @@ func syncGroupsAndDoFurtherAction(furtherAction: () -> Void) { // 增加本地没有的 Group for groupInfo in allGroups { - - let group = syncGroupWithGroupInfo(groupInfo, inRealm: realm) - - group?.includeMe = true - - //Sync Feed - - if let - feedInfo = groupInfo["topic"] as? JSONDictionary, - feed = DiscoveredFeed.fromFeedInfo(feedInfo, groupInfo: groupInfo), - group = group { - //saveFeedWithFeedDataWithFullGroup(feedData, group: group, inRealm: realm) - saveFeedWithDiscoveredFeed(feed, group: group, inRealm: realm) - } else { - println("no sync feed from groupInfo: \(groupInfo)") - } + syncFeedGroupWithGroupInfo(groupInfo, inRealm: realm) } let _ = try? realm.commitWrite() @@ -590,6 +649,25 @@ func syncGroupsAndDoFurtherAction(furtherAction: () -> Void) { } } +func syncFeedGroupWithGroupInfo(groupInfo: JSONDictionary, inRealm realm: Realm) { + + let group = syncGroupWithGroupInfo(groupInfo, inRealm: realm) + + group?.includeMe = true + + //Sync Feed + + if let + feedInfo = groupInfo["topic"] as? JSONDictionary, + feed = DiscoveredFeed.fromFeedInfo(feedInfo, groupInfo: groupInfo), + group = group { + saveFeedWithDiscoveredFeed(feed, group: group, inRealm: realm) + + } else { + println("no sync feed from groupInfo: \(groupInfo)") + } +} + func syncGroupWithGroupInfo(groupInfo: JSONDictionary, inRealm realm: Realm) -> Group? { if let groupID = groupInfo["id"] as? String { @@ -900,7 +978,7 @@ func recordMessageWithMessageID(messageID: String, detailInfo messageInfo: JSOND } } -func syncMessageWithMessageInfo(messageInfo: JSONDictionary, messageAge: MessageAge, inRealm realm: Realm, andDoFurtherAction furtherAction: ((messageIDs: [String]) -> Void)? ) { +func syncMessageWithMessageInfo(messageInfo: JSONDictionary, messageAge: MessageAge, inRealm realm: Realm, andDoFurtherAction furtherAction: ((messageIDs: [String]) -> Void)?) { if let messageID = messageInfo["id"] as? String { diff --git a/Yep/Shortcuts/ShortcutItems.swift b/Yep/Shortcuts/ShortcutItems.swift new file mode 100644 index 000000000..5ae8b336d --- /dev/null +++ b/Yep/Shortcuts/ShortcutItems.swift @@ -0,0 +1,215 @@ +// +// ShortcutItems.swift +// Yep +// +// Created by NIX on 16/4/28. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import UIKit +import RealmSwift +import DeviceGuru + +func configureDynamicShortcuts() { + + guard DeviceGuru.hardware().yep_supportQuickAction else { + UIApplication.sharedApplication().shortcutItems = nil + return + } + + var shortcutItems = [UIApplicationShortcutItem]() + + do { + let type = ShortcutType.Feeds.rawValue + + let item = UIApplicationShortcutItem( + type: type, + localizedTitle: NSLocalizedString("Feeds", comment: ""), + localizedSubtitle: nil, + icon: UIApplicationShortcutIcon(templateImageName: "icon_feeds_active"), + userInfo: nil + ) + + shortcutItems.append(item) + } + + do { + if let realm = try? Realm() { + + realm.refresh() + + let oneToOneConversations = oneToOneConversationsInRealm(realm) + + let a = oneToOneConversations[safe: 0] + let b = oneToOneConversations[safe: 1] + let c = oneToOneConversations[safe: 2] + + let feedConversations = feedConversationsInRealm(realm) + + let d = feedConversations[safe: 0] + let e = feedConversations[safe: 1] + let f = feedConversations[safe: 2] + + let conversations = [a, b, c, d, e, f].flatMap({ $0 }).sort({ $0.updatedUnixTime > $1.updatedUnixTime }) + + for (index, conversation) in conversations.enumerate() { + + if index > 2 { + break + } + + if let user = conversation.withFriend { + + let type = ShortcutType.LatestOneToOneConversation.rawValue + + let textMessageOrUpdatedTime = conversation.latestValidMessage?.textContent ?? + NSDate(timeIntervalSince1970: conversation.updatedUnixTime).timeAgo + + let item = UIApplicationShortcutItem( + type: type, + localizedTitle: user.nickname, + localizedSubtitle: textMessageOrUpdatedTime, + icon: UIApplicationShortcutIcon(templateImageName: "icon_chat_active"), + userInfo: ["userID": user.userID] + ) + + shortcutItems.append(item) + + } else if let feed = conversation.withGroup?.withFeed { + + let type = ShortcutType.LatestFeedConversation.rawValue + + let textMessageOrUpdatedTime = conversation.latestValidMessage?.textContent ?? + NSDate(timeIntervalSince1970: conversation.updatedUnixTime).timeAgo + + let item = UIApplicationShortcutItem( + type: type, + localizedTitle: feed.body, + localizedSubtitle: textMessageOrUpdatedTime, + icon: UIApplicationShortcutIcon(templateImageName: "icon_discussion"), + userInfo: ["feedID": feed.feedID] + ) + + shortcutItems.append(item) + } + } + } + } + + UIApplication.sharedApplication().shortcutItems = shortcutItems +} + +func tryQuickActionWithShortcutItem(shortcutItem: UIApplicationShortcutItem, inWindow window: UIWindow) { + + guard let shortcutType = ShortcutType(rawValue: shortcutItem.type) else { + return + } + + switch shortcutType { + + case .Feeds: + + guard let tabBarVC = window.rootViewController as? YepTabBarController else { + break + } + + tabBarVC.tab = .Feeds + + if let nvc = tabBarVC.selectedViewController as? UINavigationController { + + func tryScrollsToTopOfFeedsViewController() { + + if let vc = nvc.topViewController as? FeedsViewController { + tabBarVC.tryScrollsToTopOfFeedsViewController(vc) + } + } + + if nvc.viewControllers.count > 1 { + nvc.popToRootViewControllerAnimated(false) + + tryScrollsToTopOfFeedsViewController() + + } else { + tryScrollsToTopOfFeedsViewController() + } + } + + case .LatestOneToOneConversation: + + guard let tabBarVC = window.rootViewController as? YepTabBarController else { + break + } + + tabBarVC.tab = .Conversations + + if let nvc = tabBarVC.selectedViewController as? UINavigationController { + + func tryShowConversationFromConversationsViewController(vc: ConversationsViewController) { + + if let userID = shortcutItem.userInfo?["userID"] as? String { + if let realm = try? Realm() { + let user = userWithUserID(userID, inRealm: realm) + if let conversation = user?.conversation { + vc.performSegueWithIdentifier("showConversation", sender: conversation) + } + } + } + } + + if nvc.viewControllers.count > 1 { + nvc.popToRootViewControllerAnimated(false) + + if let vc = nvc.topViewController as? ConversationsViewController { + tryShowConversationFromConversationsViewController(vc) + } + + } else { + if let vc = nvc.topViewController as? ConversationsViewController { + tryShowConversationFromConversationsViewController(vc) + } + } + } + + case .LatestFeedConversation: + + guard let tabBarVC = window.rootViewController as? YepTabBarController else { + break + } + + tabBarVC.tab = .Conversations + + if let nvc = tabBarVC.selectedViewController as? UINavigationController { + + func tryShowConversationFromConversationsViewController(vc: ConversationsViewController) { + + if let feedID = shortcutItem.userInfo?["feedID"] as? String { + if let realm = try? Realm() { + let feed = feedWithFeedID(feedID, inRealm: realm) + if let conversation = feed?.group?.conversation { + vc.performSegueWithIdentifier("showConversation", sender: conversation) + } + } + } + } + + if nvc.viewControllers.count > 1 { + nvc.popToRootViewControllerAnimated(false) + + if let vc = nvc.topViewController as? ConversationsViewController { + tryShowConversationFromConversationsViewController(vc) + } + + } else { + if let vc = nvc.topViewController as? ConversationsViewController { + tryShowConversationFromConversationsViewController(vc) + } + } + } + } +} + +func clearDynamicShortcuts() { + + UIApplication.sharedApplication().shortcutItems = nil +} + diff --git a/Yep/Shortcuts/ShortcutType.swift b/Yep/Shortcuts/ShortcutType.swift new file mode 100644 index 000000000..101fcb977 --- /dev/null +++ b/Yep/Shortcuts/ShortcutType.swift @@ -0,0 +1,17 @@ +// +// ShortcutType.swift +// Yep +// +// Created by NIX on 16/4/28. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import UIKit + +enum ShortcutType: String { + + case Feeds = "com.Catch-Inc.Yep.Shortcut.Feeds" + case LatestOneToOneConversation = "com.Catch-Inc.Yep.Shortcut.LatestOneToOneConversation" + case LatestFeedConversation = "com.Catch-Inc.Yep.Shortcut.LatestFeedConversation" +} + diff --git a/Yep/ViewControllers/Feeds/FeedsSearchTransition.swift b/Yep/Transitions/SearchTransition.swift similarity index 80% rename from Yep/ViewControllers/Feeds/FeedsSearchTransition.swift rename to Yep/Transitions/SearchTransition.swift index 9dd085114..c78f4b819 100644 --- a/Yep/ViewControllers/Feeds/FeedsSearchTransition.swift +++ b/Yep/Transitions/SearchTransition.swift @@ -1,30 +1,30 @@ // -// FeedsSearchTransition.swift +// SearchTransition.swift // Yep // -// Created by NIX on 16/4/11. +// Created by NIX on 16/4/22. // Copyright © 2016年 Catch Inc. All rights reserved. // import UIKit -class FeedsSearchTransition: NSObject { +final class SearchTransition: NSObject { var isPresentation = true } -extension FeedsSearchTransition: UINavigationControllerDelegate { +extension SearchTransition: UINavigationControllerDelegate { func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { if operation == .Push { - if (fromVC is FeedsViewController) && (toVC is SearchFeedsViewController) { + if (fromVC is SearchTriggerRepresentation) && (toVC is SearchActionRepresentation) { isPresentation = true return self } } else if operation == .Pop { - if (fromVC is SearchFeedsViewController) && (toVC is FeedsViewController) { + if (fromVC is SearchActionRepresentation) && (toVC is SearchTriggerRepresentation) { isPresentation = false return self } @@ -34,7 +34,7 @@ extension FeedsSearchTransition: UINavigationControllerDelegate { } } -extension FeedsSearchTransition: UIViewControllerAnimatedTransitioning { +extension SearchTransition: UIViewControllerAnimatedTransitioning { func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { @@ -79,7 +79,8 @@ extension FeedsSearchTransition: UIViewControllerAnimatedTransitioning { private func dismissTransition(transitionContext: UIViewControllerContextTransitioning) { - let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as! SearchFeedsViewController + let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! + let searchActionRepresentation = fromVC as! SearchActionRepresentation let containerView = transitionContext.containerView()! @@ -90,13 +91,13 @@ extension FeedsSearchTransition: UIViewControllerAnimatedTransitioning { containerView.addSubview(fromView) fromView.alpha = 1 - fromVC.searchBar.setShowsCancelButton(false, animated: true) + searchActionRepresentation.searchBar.setShowsCancelButton(false, animated: true) let fullDuration = transitionDuration(transitionContext) UIView.animateWithDuration(fullDuration * 0.6, delay: 0.0, options: [.CurveEaseInOut], animations: { _ in - fromVC.searchBarTopConstraint.constant = 44 + searchActionRepresentation.searchBarTopConstraint.constant = 44 fromVC.view.layoutIfNeeded() }, completion: { finished in @@ -109,4 +110,5 @@ extension FeedsSearchTransition: UIViewControllerAnimatedTransitioning { }) }) } -} \ No newline at end of file +} + diff --git a/Yep/ViewControllers/About/AboutViewController.swift b/Yep/ViewControllers/About/AboutViewController.swift index b9ec09e93..cc1913385 100644 --- a/Yep/ViewControllers/About/AboutViewController.swift +++ b/Yep/ViewControllers/About/AboutViewController.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class AboutViewController: SegueViewController { +final class AboutViewController: SegueViewController { @IBOutlet private weak var appLogoImageView: UIImageView! @IBOutlet private weak var appLogoImageViewTopConstraint: NSLayoutConstraint! @@ -28,7 +28,7 @@ class AboutViewController: SegueViewController { private let rowHeight: CGFloat = Ruler.iPhoneVertical(50, 60, 60, 60).value private let aboutAnnotations: [String] = [ - NSLocalizedString("Pods help Yep", comment: ""), + NSLocalizedString("Pods helps Yep", comment: ""), NSLocalizedString("Rate Yep on App Store", comment: ""), NSLocalizedString("Terms of Service", comment: ""), ] diff --git a/Yep/ViewControllers/AddFriends/AddFriendsViewController.swift b/Yep/ViewControllers/AddFriends/AddFriendsViewController.swift index a414b2361..b0f292a7d 100644 --- a/Yep/ViewControllers/AddFriends/AddFriendsViewController.swift +++ b/Yep/ViewControllers/AddFriends/AddFriendsViewController.swift @@ -10,7 +10,7 @@ import UIKit import AddressBook import Proposer -class AddFriendsViewController: SegueViewController { +final class AddFriendsViewController: SegueViewController { private let addFriendSearchCellIdentifier = "AddFriendSearchCell" private let addFriendMoreCellIdentifier = "AddFriendMoreCell" @@ -30,6 +30,36 @@ class AddFriendsViewController: SegueViewController { title = NSLocalizedString("Add Friends", comment: "") } + private var isFirstAppear: Bool = true + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + if isFirstAppear { + delay(0.2) { [weak self] in + self?.tryShowKeyboard() + } + } + + isFirstAppear = false + } + + private var addFriendSearchCell: AddFriendSearchCell? { + + let searchIndexPath = NSIndexPath(forRow: 0, inSection: Section.Search.rawValue) + return addFriendsTableView.cellForRowAtIndexPath(searchIndexPath) as? AddFriendSearchCell + } + + private func tryShowKeyboard() { + + addFriendSearchCell?.searchTextField.becomeFirstResponder() + } + + private func tryHideKeyboard() { + + addFriendSearchCell?.searchTextField.resignFirstResponder() + } + // MARK: Navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { @@ -91,9 +121,6 @@ extension AddFriendsViewController: UITableViewDataSource, UITableViewDelegate { cell.searchTextField.returnKeyType = .Search cell.searchTextField.delegate = self - delay(0.5) { - cell.searchTextField.becomeFirstResponder() - } return cell @@ -115,6 +142,8 @@ extension AddFriendsViewController: UITableViewDataSource, UITableViewDelegate { tableView.deselectRowAtIndexPath(indexPath, animated: true) } + tryHideKeyboard() + if indexPath.section == Section.More.rawValue { switch indexPath.row { diff --git a/Yep/ViewControllers/Base/BaseViewController.swift b/Yep/ViewControllers/Base/BaseViewController.swift index 9fd13f807..f4823ff82 100644 --- a/Yep/ViewControllers/Base/BaseViewController.swift +++ b/Yep/ViewControllers/Base/BaseViewController.swift @@ -31,7 +31,7 @@ class BaseViewController: SegueViewController { navigationController.navigationBar.barStyle = UIBarStyle.Default navigationController.navigationBar.setBackgroundImage(nil, forBarMetrics: UIBarMetrics.Default) - let textAttributes = [ + let textAttributes: [String: AnyObject] = [ NSForegroundColorAttributeName: UIColor.yepNavgationBarTitleColor(), NSFontAttributeName: UIFont.navigationBarTitleFont() ] diff --git a/Yep/ViewControllers/BlackList/BlackListViewController.swift b/Yep/ViewControllers/BlackList/BlackListViewController.swift index 28cd69873..d189339f4 100644 --- a/Yep/ViewControllers/BlackList/BlackListViewController.swift +++ b/Yep/ViewControllers/BlackList/BlackListViewController.swift @@ -9,14 +9,14 @@ import UIKit import RealmSwift -class BlackListViewController: BaseViewController { +final class BlackListViewController: BaseViewController { @IBOutlet private weak var blockedUsersTableView: UITableView! @IBOutlet private weak var activityIndicator: UIActivityIndicatorView! private let cellIdentifier = "ContactsCell" - private var blockedUsers = [DiscoveredUser]() { + private var blockedUsers: [DiscoveredUser] = [] { willSet { if newValue.count == 0 { blockedUsersTableView.tableFooterView = InfoView(NSLocalizedString("No blocked users.", comment: "")) diff --git a/Yep/ViewControllers/ChangeMobile/ChangeMobile.storyboard b/Yep/ViewControllers/ChangeMobile/ChangeMobile.storyboard new file mode 100644 index 000000000..4b261af1a --- /dev/null +++ b/Yep/ViewControllers/ChangeMobile/ChangeMobile.storyboard @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yep/ViewControllers/ChangeMobile/ChangeMobileViewController.swift b/Yep/ViewControllers/ChangeMobile/ChangeMobileViewController.swift new file mode 100644 index 000000000..bdffa2fe3 --- /dev/null +++ b/Yep/ViewControllers/ChangeMobile/ChangeMobileViewController.swift @@ -0,0 +1,202 @@ +// +// ChangeMobileViewController.swift +// Yep +// +// Created by NIX on 16/5/4. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import UIKit +import Ruler + +class ChangeMobileViewController: UIViewController { + + @IBOutlet private weak var changeMobileNumberPromptLabel: UILabel! + @IBOutlet private weak var changeMobileNumberPromptLabelTopConstraint: NSLayoutConstraint! + + @IBOutlet private weak var currentMobileNumberPromptLabel: UILabel! + @IBOutlet private weak var currentMobileNumberLabel: UILabel! + + @IBOutlet private weak var areaCodeTextField: BorderTextField! + @IBOutlet private weak var areaCodeTextFieldWidthConstraint: NSLayoutConstraint! + + @IBOutlet private weak var mobileNumberTextField: BorderTextField! + @IBOutlet private weak var mobileNumberTextFieldTopConstraint: NSLayoutConstraint! + + private lazy var nextButton: UIBarButtonItem = { + let button = UIBarButtonItem(title: NSLocalizedString("Next", comment: ""), style: .Plain, target: self, action: #selector(ChangeMobileViewController.next(_:))) + return button + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor.yepViewBackgroundColor() + + navigationItem.titleView = NavigationTitleLabel(title: NSLocalizedString("Change Mobile", comment: "")) + + navigationItem.rightBarButtonItem = nextButton + + changeMobileNumberPromptLabel.text = NSLocalizedString("What's your new number?", comment: "") + + currentMobileNumberPromptLabel.text = NSLocalizedString("Current number:", comment: "") + currentMobileNumberLabel.text = YepUserDefaults.fullPhoneNumber + + areaCodeTextField.text = NSTimeZone.areaCode + areaCodeTextField.backgroundColor = UIColor.whiteColor() + + areaCodeTextField.delegate = self + areaCodeTextField.addTarget(self, action: #selector(ChangeMobileViewController.textFieldDidChange(_:)), forControlEvents: .EditingChanged) + + mobileNumberTextField.placeholder = "" + mobileNumberTextField.backgroundColor = UIColor.whiteColor() + mobileNumberTextField.textColor = UIColor.yepInputTextColor() + mobileNumberTextField.delegate = self + mobileNumberTextField.addTarget(self, action: #selector(ChangeMobileViewController.textFieldDidChange(_:)), forControlEvents: .EditingChanged) + + changeMobileNumberPromptLabelTopConstraint.constant = Ruler.iPhoneVertical(30, 50, 60, 60).value + mobileNumberTextFieldTopConstraint.constant = Ruler.iPhoneVertical(30, 40, 50, 50).value + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + + nextButton.enabled = false + } + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + mobileNumberTextField.becomeFirstResponder() + } + + // MARK: Actions + + private func adjustAreaCodeTextFieldWidth() { + guard let text = areaCodeTextField.text else { + return + } + + let size = text.sizeWithAttributes(areaCodeTextField.editing ? areaCodeTextField.typingAttributes : areaCodeTextField.defaultTextAttributes) + + let width = 32 + (size.width + 22) + 20 + + UIView.animateWithDuration(0.1, delay: 0.0, options: .CurveEaseInOut, animations: { _ in + self.areaCodeTextFieldWidthConstraint.constant = max(width, 100) + self.view.layoutIfNeeded() + }, completion: { finished in + }) + } + + @objc private func textFieldDidChange(textField: UITextField) { + + guard let areaCode = areaCodeTextField.text, mobile = mobileNumberTextField.text else { + return + } + + nextButton.enabled = !areaCode.isEmpty && !mobile.isEmpty + + if textField == areaCodeTextField { + adjustAreaCodeTextFieldWidth() + } + } + + @objc private func next(sender: UIBarButtonItem) { + tryShowVerifyChangedMobile() + } + + private func tryShowVerifyChangedMobile() { + + view.endEditing(true) + + guard let areaCode = areaCodeTextField.text, mobile = mobileNumberTextField.text else { + return + } + + YepHUD.showActivityIndicator() + + sendVerifyCodeOfNewMobile(mobile, withAreaCode: areaCode, useMethod: .SMS, failureHandler: { [weak self] reason, errorMessage in + defaultFailureHandler(reason: reason, errorMessage: errorMessage) + + YepHUD.hideActivityIndicator() + + let errorMessage = errorMessage ?? NSLocalizedString("Failed to send verification code!", comment: "") + YepAlert.alertSorry(message: errorMessage, inViewController: self, withDismissAction: { () -> Void in + dispatch_async(dispatch_get_main_queue()) { + self?.mobileNumberTextField.becomeFirstResponder() + } + }) + + }, completion: { [weak self] in + + YepHUD.hideActivityIndicator() + + dispatch_async(dispatch_get_main_queue()) { + self?.showVerifyChangedMobile() + } + }) + } + + private func showVerifyChangedMobile() { + + guard let areaCode = areaCodeTextField.text, mobile = mobileNumberTextField.text else { + return + } + + performSegueWithIdentifier("showVerifyChangedMobile", sender: ["mobile" : mobile, "areaCode": areaCode]) + } + + // MARK: Navigation + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + + if segue.identifier == "showVerifyChangedMobile" { + + if let info = sender as? [String: String] { + let vc = segue.destinationViewController as! VerifyChangedMobileViewController + + vc.mobile = info["mobile"] + vc.areaCode = info["areaCode"] + } + } + } +} + +// MARK: - UITextFieldDelegate + +extension ChangeMobileViewController: UITextFieldDelegate { + + func textFieldShouldBeginEditing(textField: UITextField) -> Bool { + + if textField == areaCodeTextField { + adjustAreaCodeTextFieldWidth() + } + + return true + } + + func textFieldDidEndEditing(textField: UITextField) { + + if textField == areaCodeTextField { + UIView.animateWithDuration(0.1, delay: 0.0, options: .CurveEaseInOut, animations: { _ in + self.areaCodeTextFieldWidthConstraint.constant = 60 + self.view.layoutIfNeeded() + }, completion: { finished in + }) + } + } + + func textFieldShouldReturn(textField: UITextField) -> Bool { + + guard let areaCode = areaCodeTextField.text, mobile = mobileNumberTextField.text else { + return true + } + + if !areaCode.isEmpty && !mobile.isEmpty { + tryShowVerifyChangedMobile() + } + + return true + } +} + diff --git a/Yep/ViewControllers/Contacts/ContactsSearchTransition.swift b/Yep/ViewControllers/Contacts/ContactsSearchTransition.swift deleted file mode 100644 index 787d13747..000000000 --- a/Yep/ViewControllers/Contacts/ContactsSearchTransition.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// ContactsSearchTransition.swift -// Yep -// -// Created by NIX on 16/3/21. -// Copyright © 2016年 Catch Inc. All rights reserved. -// - -import UIKit - -class ContactsSearchTransition: NSObject { - - var isPresentation = true -} - -extension ContactsSearchTransition: UINavigationControllerDelegate { - - func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { - - if operation == .Push { - if (fromVC is ContactsViewController) && (toVC is SearchContactsViewController) { - isPresentation = true - return self - } - - } else if operation == .Pop { - if (fromVC is SearchContactsViewController) && (toVC is ContactsViewController) { - isPresentation = false - return self - } - } - - return nil - } -} - -extension ContactsSearchTransition: UIViewControllerAnimatedTransitioning { - - func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { - - if isPresentation { - return 0.25 - } else { - return 0.45 - } - } - - func animateTransition(transitionContext: UIViewControllerContextTransitioning) { - - if isPresentation { - presentTransition(transitionContext) - - } else { - dismissTransition(transitionContext) - } - } - - private func presentTransition(transitionContext: UIViewControllerContextTransitioning) { - - //let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as! SearchContactsViewController - - let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! - - let containerView = transitionContext.containerView()! - - containerView.addSubview(toView) - - toView.alpha = 0 - - let fullDuration = transitionDuration(transitionContext) - - UIView.animateWithDuration(fullDuration, delay: 0.0, options: [.CurveEaseInOut, .LayoutSubviews], animations: { _ in - toView.alpha = 1 - - }, completion: { finished in - transitionContext.completeTransition(true) - }) - } - - private func dismissTransition(transitionContext: UIViewControllerContextTransitioning) { - - let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as! SearchContactsViewController - - let containerView = transitionContext.containerView()! - - let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! - let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! - - containerView.addSubview(toView) - containerView.addSubview(fromView) - - fromView.alpha = 1 - fromVC.searchBar.setShowsCancelButton(false, animated: true) - - let fullDuration = transitionDuration(transitionContext) - - UIView.animateWithDuration(fullDuration * 0.6, delay: 0.0, options: [.CurveEaseInOut], animations: { _ in - - fromVC.searchBarTopConstraint.constant = 44 - fromVC.view.layoutIfNeeded() - - }, completion: { finished in - - UIView.animateWithDuration(fullDuration * 0.4, delay: 0.0, options: [.CurveEaseInOut], animations: { _ in - fromView.alpha = 0 - - }, completion: { finished in - transitionContext.completeTransition(true) - }) - }) - - /* - UIView.animateKeyframesWithDuration(fullDuration, delay: 0, options: [.CalculationModeCubic], animations: { - - UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.6, animations: { - fromVC.searchBarTopConstraint.constant = 44 - fromVC.view.layoutIfNeeded() - }) - - UIView.addKeyframeWithRelativeStartTime(0.6, relativeDuration: 0.4, animations: { - fromView.alpha = 0 - }) - - }, completion: { finished in - transitionContext.completeTransition(true) - }) - */ - - /* - UIView.animateWithDuration(fullDuration, delay: 0.0, options: [.CurveEaseInOut, .LayoutSubviews], animations: { _ in - //fromView.alpha = 0 - - fromVC.searchBarTopConstraint.constant = 44 - fromVC.view.layoutIfNeeded() - - }, completion: { finished in - fromView.alpha = 0 - - transitionContext.completeTransition(true) - }) - */ - } -} - diff --git a/Yep/ViewControllers/Contacts/ContactsViewController.swift b/Yep/ViewControllers/Contacts/ContactsViewController.swift index daa281f5c..fdf421726 100644 --- a/Yep/ViewControllers/Contacts/ContactsViewController.swift +++ b/Yep/ViewControllers/Contacts/ContactsViewController.swift @@ -9,16 +9,14 @@ import UIKit import RealmSwift import Ruler -import KeyboardMan -class ContactsViewController: BaseViewController { +final class ContactsViewController: BaseViewController { @IBOutlet weak var contactsTableView: UITableView! @IBOutlet private weak var coverUnderStatusBarView: UIView! var conversationToShare: Conversation? - #if DEBUG private lazy var contactsFPSLabel: FPSLabel = { let label = FPSLabel() @@ -31,20 +29,17 @@ class ContactsViewController: BaseViewController { return searchController?.active ?? false } - private var originalNavigationControllerDelegate: UINavigationControllerDelegate? - private lazy var contactsSearchTransition: ContactsSearchTransition = { - return ContactsSearchTransition() + var originalNavigationControllerDelegate: UINavigationControllerDelegate? + lazy var searchTransition: SearchTransition = { + return SearchTransition() }() -// private let keyboardMan = KeyboardMan() -// private var normalContactsTableViewContentInsetBottom: CGFloat? - private let cellIdentifier = "ContactsCell" private lazy var friends = normalFriends() private var filteredFriends: Results? - private var searchedUsers = [DiscoveredUser]() + private var searchedUsers: [DiscoveredUser] = [] private var realmNotificationToken: NotificationToken? @@ -150,19 +145,6 @@ class ContactsViewController: BaseViewController { } } -// keyboardMan.animateWhenKeyboardAppear = { [weak self] _, keyboardHeight, _ in -// self?.normalContactsTableViewContentInsetBottom = self?.contactsTableView.contentInset.bottom -// self?.contactsTableView.contentInset.bottom = keyboardHeight -// self?.contactsTableView.scrollIndicatorInsets.bottom = keyboardHeight -// } -// -// keyboardMan.animateWhenKeyboardDisappear = { [weak self] _ in -// if let bottom = self?.normalContactsTableViewContentInsetBottom { -// self?.contactsTableView.contentInset.bottom = bottom -// self?.contactsTableView.scrollIndicatorInsets.bottom = bottom -// } -// } - #if DEBUG //view.addSubview(contactsFPSLabel) #endif @@ -171,7 +153,7 @@ class ContactsViewController: BaseViewController { override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() } // MARK: Actions @@ -206,25 +188,12 @@ class ContactsViewController: BaseViewController { // MARK: Navigation - private func recoverNavigationDelegate() { - if let originalNavigationControllerDelegate = originalNavigationControllerDelegate { - navigationController?.delegate = originalNavigationControllerDelegate - } - } - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { guard let identifier = segue.identifier else { return } - - func hackNavigationDelegate() { - // 在自定义 push 之前,记录原始的 NavigationControllerDelegate 以便 pop 后恢复 - originalNavigationControllerDelegate = navigationController?.delegate - - navigationController?.delegate = contactsSearchTransition - } - + switch identifier { case "showConversation": @@ -233,6 +202,10 @@ class ContactsViewController: BaseViewController { } let vc = segue.destinationViewController as! ConversationViewController vc.conversationToShare = self.conversationToShare + if self.conversationToShare != nil { + + + } if let user = sender as? User { if user.userID != YepUserDefaults.userID.value { if user.friendState != UserFriendState.Me.rawValue { @@ -248,11 +221,12 @@ class ContactsViewController: BaseViewController { } } vc.conversation = user.conversation - print(FeedKind(rawValue:vc.conversationToShare!.withGroup!.withFeed!.kind),"___Toshare") NSNotificationCenter.defaultCenter().postNotificationName(YepConfig.Notification.changedConversation, object: nil) } } } + + recoverOriginalNavigationDelegate() case "showProfile": let vc = segue.destinationViewController as! ProfileViewController @@ -270,7 +244,7 @@ class ContactsViewController: BaseViewController { vc.setBackButtonWithTitle() - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() case "showSearchContacts": @@ -279,7 +253,7 @@ class ContactsViewController: BaseViewController { vc.hidesBottomBarWhenPushed = true - hackNavigationDelegate() + prepareSearchTransition() default: break @@ -411,27 +385,37 @@ extension ContactsViewController: UITableViewDataSource, UITableViewDelegate { defer { tableView.deselectRowAtIndexPath(indexPath, animated: true) } - + guard let section = Section(rawValue: indexPath.section) else { return } - + switch section { - + case .Local: - + if let friend = friendAtIndexPath(indexPath) { searchController?.active = false - performSegueWithIdentifier("showProfile", sender: friend) + if self.conversationToShare != nil { + YepAlert.confirmOrCancel(title: NSLocalizedString("Notice", comment: ""), message: NSLocalizedString("确定发送?", comment: ""), confirmTitle: NSLocalizedString("Yes", comment: ""), cancelTitle: NSLocalizedString("Cancel", comment: ""), inViewController: self, withConfirmAction: { [weak self] in + + self?.performSegueWithIdentifier("showConversation", sender: friend) + + }, cancelAction: { () -> Void in + + }) + } else { + performSegueWithIdentifier("showProfile", sender: friend) + } } - + case .Online: - + let discoveredUser = searchedUsers[indexPath.row] searchController?.active = false performSegueWithIdentifier("showProfile", sender: Box(discoveredUser)) } - } + } } // MARK: - UISearchResultsUpdating @@ -462,7 +446,7 @@ extension ContactsViewController: UISearchResultsUpdating { // 剔除 filteredFriends 里已有的 - var searchedUsers = [DiscoveredUser]() + var searchedUsers: [DiscoveredUser] = [] let filteredFriendUserIDSet = Set(filteredFriends.map({ $0.userID })) diff --git a/Yep/ViewControllers/Conversation/ConversationLayout.swift b/Yep/ViewControllers/Conversation/ConversationLayout.swift index 767073363..05a048f69 100644 --- a/Yep/ViewControllers/Conversation/ConversationLayout.swift +++ b/Yep/ViewControllers/Conversation/ConversationLayout.swift @@ -9,7 +9,7 @@ import UIKit import QuartzCore -class ConversationLayout: UICollectionViewFlowLayout { +final class ConversationLayout: UICollectionViewFlowLayout { override func prepareLayout() { super.prepareLayout() diff --git a/Yep/ViewControllers/Conversation/ConversationMoreViewManager.swift b/Yep/ViewControllers/Conversation/ConversationMoreViewManager.swift index cd544a7ec..bbee75204 100644 --- a/Yep/ViewControllers/Conversation/ConversationMoreViewManager.swift +++ b/Yep/ViewControllers/Conversation/ConversationMoreViewManager.swift @@ -8,7 +8,7 @@ import Foundation -class ConversationMoreViewManager { +final class ConversationMoreViewManager { var conversation: Conversation? @@ -17,7 +17,7 @@ class ConversationMoreViewManager { var reportAction: (() -> Void)? var toggleBlockAction: (() -> Void)? var shareFeedAction: (() -> Void)? - var shareFeedToContactAction: (() -> Void)? +// var shareFeedToContactAction: (() -> Void)? var updateGroupAffairAction: (() -> Void)? var afterGotSettingsForUserAction: ((userID: String, blocked: Bool, doNotDisturb: Bool) -> Void)? @@ -106,7 +106,7 @@ class ConversationMoreViewManager { return true } ), - self.makeShareToContactItem(), + //self.makeShareToContactItem(), self.updateGroupItem(group: group), // 2 reportItem, cancelItem, @@ -172,16 +172,16 @@ class ConversationMoreViewManager { ) } - private func makeShareToContactItem() -> ActionSheetView.Item { - return .Default( - title: NSLocalizedString("Share To Contact", comment: ""), - titleColor: UIColor.yepTintColor(), - action: { [weak self] in - self?.shareFeedToContactAction?() - return false - } - ) - } +// private func makeShareToContactItem() -> ActionSheetView.Item { +// return .Default( +//// title: NSLocalizedString("Share To Contact", comment: ""), +// titleColor: UIColor.yepTintColor(), +// action: { [weak self] in +// self?.shareFeedToContactAction?() +// return false +// } +// ) +// } private func makeBlockItem(blocked blocked: Bool) -> ActionSheetView.Item { return .Default( diff --git a/Yep/ViewControllers/Conversation/ConversationViewController.swift b/Yep/ViewControllers/Conversation/ConversationViewController.swift index 52febcb9b..1269565df 100644 --- a/Yep/ViewControllers/Conversation/ConversationViewController.swift +++ b/Yep/ViewControllers/Conversation/ConversationViewController.swift @@ -226,7 +226,7 @@ enum ConversationFeed { return nil } - + var locationName: String? { switch self { @@ -309,11 +309,15 @@ enum ConversationFeed { } } -class ConversationViewController: BaseViewController { +final class ConversationViewController: BaseViewController { var conversation: Conversation! var conversationFeed: ConversationFeed? - var conversationToShare: Conversation? + var conversationToShare: Conversation? = nil { + didSet { + print( "didset___") + } + } private var recipient: Recipient? @@ -436,14 +440,14 @@ class ConversationViewController: BaseViewController { self?.toggleBlock() } - manager.shareFeedToContactAction = { [weak self] in - manager.moreView.hide() - - if let conversation = self?.conversation.withGroup?.conversation { -// let vc = UIStoryboard(name: "Contacts", bundle: nil).instantiateViewControllerWithIdentifier("ContactsViewController") as? ContactsViewController - self?.performSegueWithIdentifier("showContacts", sender: Box(conversation)) - } - } +// manager.shareFeedToContactAction = { [weak self] in +// manager.moreView.hide() +// +// if let conversation = self?.conversation.withGroup?.conversation { +//// let vc = UIStoryboard(name: "Contacts", bundle: nil).instantiateViewControllerWithIdentifier("ContactsViewController") as? ContactsViewController +// self?.performSegueWithIdentifier("showContacts", sender: Box(conversation)) +// } +// } manager.shareFeedAction = { [weak self] in guard let @@ -699,6 +703,8 @@ class ConversationViewController: BaseViewController { private let chatTextIndicatorCellIdentifier = "ChatTextIndicatorCell" private let chatLeftSocialWorkCellIdentifier = "ChatLeftSocialWorkCell" private let chatLeftShareFeedCellIdentifier = "LeftShareFeedCell" + private let chatRightShareFeedCellIdentifier = "RightShareFeedCell" + private struct Listener { static let Avatar = "ConversationViewController" @@ -972,11 +978,11 @@ class ConversationViewController: BaseViewController { super.viewWillAppear(animated) if isFirstAppear { - if let feed = conversation.withGroup?.withFeed { conversationFeed = ConversationFeed.FeedType(feed) } +// println(conversationFeed,"___conversationFeed")// 私聊时此项为空 if let conversationFeed = conversationFeed { makeFeedViewWithFeed(conversationFeed) tryFoldFeedView() @@ -1252,7 +1258,10 @@ class ConversationViewController: BaseViewController { println("\nComporessed \(audioSamples)") - let audioMetaDataInfo = [YepConfig.MetaData.audioSamples: audioSamples, YepConfig.MetaData.audioDuration: audioDuration] + let audioMetaDataInfo = [ + YepConfig.MetaData.audioSamples: audioSamples, + YepConfig.MetaData.audioDuration: audioDuration + ] if let audioMetaData = try? NSJSONSerialization.dataWithJSONObject(audioMetaDataInfo, options: []) { let audioMetaDataString = NSString(data: audioMetaData, encoding: NSUTF8StringEncoding) as? String @@ -1864,7 +1873,7 @@ class ConversationViewController: BaseViewController { view.addSubview(feedView) - let views = [ + let views: [String: AnyObject] = [ "feedView": feedView ] @@ -2057,7 +2066,9 @@ class ConversationViewController: BaseViewController { case MessageMediaType.SocialWork.rawValue: height = 135 - + + case MessageMediaType.ShareFeed.rawValue: + height = 60 default: height = 20 } @@ -3828,7 +3839,11 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftSocialWorkCellIdentifier, forIndexPath: indexPath) as! ChatLeftSocialWorkCell return cell - + +// case MessageMediaType.ShareFeed.rawValue: +// let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftShareFeedCellIdentifier, forIndexPath: indexPath) as! LeftShareFeedCell +// return cell + default: if message.deletedByCreator { @@ -3870,7 +3885,12 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightLocationCellIdentifier, forIndexPath: indexPath) as! ChatRightLocationCell return cell - + +// case MessageMediaType.ShareFeed.rawValue: +// +// let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightShareFeedCellIdentifier, forIndexPath:indexPath) as! RightShareFeedCell +// return cell + default: if message.openGraphInfo != nil { @@ -4078,6 +4098,15 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi self?.performSegueWithIdentifier("presentNewFeed", sender: socialWork) } } +// case MessageMediaType.ShareFeed.rawValue: +// +// if let cell = cell as? LeftShareFeedCell { +// cell.configureWithMessage(message, collectionView: collectionView, indexPath: indexPath) { [weak self] in +// let vc = UIStoryboard(name: "Conversation", bundle: nil).instantiateViewControllerWithIdentifier("ConversationViewController") as! ConversationViewController +// vc.conversation = self?.conversationToShare +// self?.navigationController?.pushViewController(vc, animated: true) +// } +// } default: @@ -4278,10 +4307,43 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi mapItem.openInMapsWithLaunchOptions(nil) } } - - }, collectionView: collectionView, indexPath: indexPath) + + }, collectionView: collectionView, indexPath: indexPath) } - + +// case MessageMediaType.ShareFeed.rawValue: + +// if let cell = cell as? RightShareFeedCell { +// cell.configureWithMessage(message, collectionView: collectionView, indexPath: indexPath) { [weak self] in +// +// if message.sendState == MessageSendState.Failed.rawValue { +// +// YepAlert.confirmOrCancel(title: NSLocalizedString("Action", comment: ""), message: NSLocalizedString("Resend Feed?", comment: ""), confirmTitle: NSLocalizedString("Resend", comment: ""), cancelTitle: NSLocalizedString("Cancel", comment: ""), inViewController: self, withConfirmAction: { +// +// resendMessage(message, failureHandler: { [weak self] reason, errorMessage in +// defaultFailureHandler(reason: reason, errorMessage: errorMessage) +// +// self?.promptSendMessageFailed( +// reason: reason, +// errorMessage: errorMessage, +// reserveErrorMessage: NSLocalizedString("Failed to resend feed!\nPlease make sure your device is connected to the Internet.", comment: "") +// ) +// +// }, completion: { success in +// println("resendFeed: \(success)") +// }) +// +// }, cancelAction: { +// }) +// +// } else { +// let vc = UIStoryboard(name: "Conversation", bundle: nil).instantiateViewControllerWithIdentifier("ConversationViewController") as! ConversationViewController +// vc.conversation = self?.conversationToShare +// self?.navigationController?.pushViewController(vc, animated: true) +// } +// } +// } + default: let mediaTapAction: () -> Void = { [weak self] in diff --git a/Yep/ViewControllers/Conversations/ConversationsSearchTransition.swift b/Yep/ViewControllers/Conversations/ConversationsSearchTransition.swift deleted file mode 100644 index 5d695f0b0..000000000 --- a/Yep/ViewControllers/Conversations/ConversationsSearchTransition.swift +++ /dev/null @@ -1,112 +0,0 @@ -// -// ConversationsSearchTransition.swift -// Yep -// -// Created by NIX on 16/4/1. -// Copyright © 2016年 Catch Inc. All rights reserved. -// - -import UIKit - -class ConversationsSearchTransition: NSObject { - - var isPresentation = true -} - -extension ConversationsSearchTransition: UINavigationControllerDelegate { - - func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { - - if operation == .Push { - if (fromVC is ConversationsViewController) && (toVC is SearchConversationsViewController) { - isPresentation = true - return self - } - - } else if operation == .Pop { - if (fromVC is SearchConversationsViewController) && (toVC is ConversationsViewController) { - isPresentation = false - return self - } - } - - return nil - } -} - -extension ConversationsSearchTransition: UIViewControllerAnimatedTransitioning { - - func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { - - if isPresentation { - return 0.25 - } else { - return 0.45 - } - } - - func animateTransition(transitionContext: UIViewControllerContextTransitioning) { - - if isPresentation { - presentTransition(transitionContext) - - } else { - dismissTransition(transitionContext) - } - } - - private func presentTransition(transitionContext: UIViewControllerContextTransitioning) { - - //let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as! SearchConversationsViewController - - let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! - - let containerView = transitionContext.containerView()! - - containerView.addSubview(toView) - - toView.alpha = 0 - - let fullDuration = transitionDuration(transitionContext) - - UIView.animateWithDuration(fullDuration, delay: 0.0, options: [.CurveEaseInOut, .LayoutSubviews], animations: { _ in - toView.alpha = 1 - - }, completion: { finished in - transitionContext.completeTransition(true) - }) - } - - private func dismissTransition(transitionContext: UIViewControllerContextTransitioning) { - - let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as! SearchConversationsViewController - - let containerView = transitionContext.containerView()! - - let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! - let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! - - containerView.addSubview(toView) - containerView.addSubview(fromView) - - fromView.alpha = 1 - fromVC.searchBar.setShowsCancelButton(false, animated: true) - - let fullDuration = transitionDuration(transitionContext) - - UIView.animateWithDuration(fullDuration * 0.6, delay: 0.0, options: [.CurveEaseInOut], animations: { _ in - - fromVC.searchBarTopConstraint.constant = 44 - fromVC.view.layoutIfNeeded() - - }, completion: { finished in - - UIView.animateWithDuration(fullDuration * 0.4, delay: 0.0, options: [.CurveEaseInOut], animations: { _ in - fromView.alpha = 0 - - }, completion: { finished in - transitionContext.completeTransition(true) - }) - }) - } -} diff --git a/Yep/ViewControllers/Conversations/ConversationsViewController.swift b/Yep/ViewControllers/Conversations/ConversationsViewController.swift index 34a4f8126..400eb212f 100644 --- a/Yep/ViewControllers/Conversations/ConversationsViewController.swift +++ b/Yep/ViewControllers/Conversations/ConversationsViewController.swift @@ -15,7 +15,7 @@ import Proposer let YepNotificationCommentAction = "YepNotificationCommentAction" let YepNotificationOKAction = "YepNotificationOKAction" -class ConversationsViewController: BaseViewController { +final class ConversationsViewController: BaseViewController { private lazy var activityIndicatorTitleView = ActivityIndicatorTitleView(frame: CGRect(x: 0, y: 0, width: 120, height: 30)) @@ -28,9 +28,9 @@ class ConversationsViewController: BaseViewController { return searchBar }() - private var originalNavigationControllerDelegate: UINavigationControllerDelegate? - private lazy var conversationsSearchTransition: ConversationsSearchTransition = { - return ConversationsSearchTransition() + var originalNavigationControllerDelegate: UINavigationControllerDelegate? + lazy var searchTransition: SearchTransition = { + return SearchTransition() }() private let feedConversationDockCellID = "FeedConversationDockCell" @@ -304,7 +304,7 @@ class ConversationsViewController: BaseViewController { isFirstAppear = false - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() } private func askForNotification() { @@ -331,29 +331,19 @@ class ConversationsViewController: BaseViewController { let types = UIUserNotificationType.Badge.rawValue | UIUserNotificationType.Sound.rawValue | UIUserNotificationType.Alert.rawValue + + #if JPUSH //JPUSHService.registerForRemoteNotificationTypes(types, categories: [category]) APService.registerForRemoteNotificationTypes(types, categories: [category]) + #endif } // MARK: Navigation - private func recoverNavigationDelegate() { - if let originalNavigationControllerDelegate = originalNavigationControllerDelegate { - navigationController?.delegate = originalNavigationControllerDelegate - } - } - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { guard let identifier = segue.identifier else { return } - func hackNavigationDelegate() { - // 在自定义 push 之前,记录原始的 NavigationControllerDelegate 以便 pop 后恢复 - originalNavigationControllerDelegate = navigationController?.delegate - - navigationController?.delegate = conversationsSearchTransition - } - switch identifier { case "showSearchConversations": @@ -363,7 +353,7 @@ class ConversationsViewController: BaseViewController { vc.hidesBottomBarWhenPushed = true - hackNavigationDelegate() + prepareSearchTransition() case "showConversation": @@ -388,7 +378,7 @@ class ConversationsViewController: BaseViewController { } } - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() case "showProfile": @@ -399,7 +389,7 @@ class ConversationsViewController: BaseViewController { vc.setBackButtonWithTitle() - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() default: break @@ -601,22 +591,28 @@ extension ConversationsViewController: UITableViewDataSource, UITableViewDelegat tryDeleteOrClearHistoryOfConversation(conversation, inViewController: self, whenAfterClearedHistory: { - tableView.setEditing(false, animated: true) + dispatch_async(dispatch_get_main_queue()) { + tableView.setEditing(false, animated: true) - // update cell + // update cell - if let cell = tableView.cellForRowAtIndexPath(indexPath) as? ConversationCell { - if let conversation = self.conversations[safe: indexPath.row] { - let radius = min(CGRectGetWidth(cell.avatarImageView.bounds), CGRectGetHeight(cell.avatarImageView.bounds)) * 0.5 - cell.configureWithConversation(conversation, avatarRadius: radius, tableView: tableView, indexPath: indexPath) + if let cell = tableView.cellForRowAtIndexPath(indexPath) as? ConversationCell { + if let conversation = self.conversations[safe: indexPath.row] { + let radius = min(CGRectGetWidth(cell.avatarImageView.bounds), CGRectGetHeight(cell.avatarImageView.bounds)) * 0.5 + cell.configureWithConversation(conversation, avatarRadius: radius, tableView: tableView, indexPath: indexPath) + } } } }, afterDeleted: { - tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) + dispatch_async(dispatch_get_main_queue()) { + tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) + } }, orCanceled: { - tableView.setEditing(false, animated: true) + dispatch_async(dispatch_get_main_queue()) { + tableView.setEditing(false, animated: true) + } }) } } diff --git a/Yep/ViewControllers/CreatorsOfBlockedFeeds/CreatorsOfBlockedFeedsViewController.swift b/Yep/ViewControllers/CreatorsOfBlockedFeeds/CreatorsOfBlockedFeedsViewController.swift index 48efd18d6..625b15474 100644 --- a/Yep/ViewControllers/CreatorsOfBlockedFeeds/CreatorsOfBlockedFeedsViewController.swift +++ b/Yep/ViewControllers/CreatorsOfBlockedFeeds/CreatorsOfBlockedFeedsViewController.swift @@ -9,7 +9,7 @@ import UIKit import RealmSwift -class CreatorsOfBlockedFeedsViewController: BaseViewController { +final class CreatorsOfBlockedFeedsViewController: BaseViewController { @IBOutlet private weak var blockedCreatorsTableView: UITableView! @IBOutlet private weak var activityIndicator: UIActivityIndicatorView! diff --git a/Yep/ViewControllers/CustomNavigationBar/CustomNavigationBarViewController.swift b/Yep/ViewControllers/CustomNavigationBar/CustomNavigationBarViewController.swift index c44678f37..87368406f 100644 --- a/Yep/ViewControllers/CustomNavigationBar/CustomNavigationBarViewController.swift +++ b/Yep/ViewControllers/CustomNavigationBar/CustomNavigationBarViewController.swift @@ -8,7 +8,7 @@ import UIKit -class CustomNavigationBarViewController: UIViewController { +final class CustomNavigationBarViewController: UIViewController { override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) @@ -21,7 +21,7 @@ class CustomNavigationBarViewController: UIViewController { navigationController.navigationBar.barStyle = UIBarStyle.BlackTranslucent navigationController.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default) - let textAttributes = [ + let textAttributes: [String: AnyObject] = [ NSForegroundColorAttributeName: UIColor.whiteColor(), NSFontAttributeName: UIFont.navigationBarTitleFont() ] diff --git a/Yep/ViewControllers/Discover/DiscoverCollectionView.swift b/Yep/ViewControllers/Discover/DiscoverCollectionView.swift index f5386fc43..fd7d2017f 100644 --- a/Yep/ViewControllers/Discover/DiscoverCollectionView.swift +++ b/Yep/ViewControllers/Discover/DiscoverCollectionView.swift @@ -8,7 +8,7 @@ import UIKit -class DiscoverCollectionView: UICollectionView { +final class DiscoverCollectionView: UICollectionView { // ref http://stackoverflow.com/questions/19483511/uirefreshcontrol-with-uicollectionview-in-ios7 override var contentInset: UIEdgeInsets { diff --git a/Yep/ViewControllers/Discover/DiscoverFlowLayout.swift b/Yep/ViewControllers/Discover/DiscoverFlowLayout.swift index d2b707c8c..68636f20b 100644 --- a/Yep/ViewControllers/Discover/DiscoverFlowLayout.swift +++ b/Yep/ViewControllers/Discover/DiscoverFlowLayout.swift @@ -8,7 +8,7 @@ import UIKit -class DiscoverFlowLayout: UICollectionViewFlowLayout { +final class DiscoverFlowLayout: UICollectionViewFlowLayout { var userMode: DiscoverUserMode? diff --git a/Yep/ViewControllers/Discover/DiscoverViewController.swift b/Yep/ViewControllers/Discover/DiscoverViewController.swift index d66943bf4..af420f3e6 100644 --- a/Yep/ViewControllers/Discover/DiscoverViewController.swift +++ b/Yep/ViewControllers/Discover/DiscoverViewController.swift @@ -16,7 +16,7 @@ enum DiscoverUserMode: Int { var skillSizeCache = [String: CGRect]() -class DiscoverViewController: BaseViewController { +final class DiscoverViewController: BaseViewController { @IBOutlet weak var discoveredUsersCollectionView: DiscoverCollectionView! diff --git a/Yep/ViewControllers/DoNotDisturbPeriod/DoNotDisturbPeriodViewController.swift b/Yep/ViewControllers/DoNotDisturbPeriod/DoNotDisturbPeriodViewController.swift index b9e0b0351..593f10587 100644 --- a/Yep/ViewControllers/DoNotDisturbPeriod/DoNotDisturbPeriodViewController.swift +++ b/Yep/ViewControllers/DoNotDisturbPeriod/DoNotDisturbPeriodViewController.swift @@ -9,7 +9,7 @@ import UIKit import RealmSwift -class DoNotDisturbPeriodViewController: UIViewController { +final class DoNotDisturbPeriodViewController: UIViewController { var doNotDisturbPeriod = DoNotDisturbPeriod() diff --git a/Yep/ViewControllers/EditNicknameAndBadge/EditNicknameAndBadgeViewController.swift b/Yep/ViewControllers/EditNicknameAndBadge/EditNicknameAndBadgeViewController.swift index 13624be06..38a2e4da2 100644 --- a/Yep/ViewControllers/EditNicknameAndBadge/EditNicknameAndBadgeViewController.swift +++ b/Yep/ViewControllers/EditNicknameAndBadge/EditNicknameAndBadgeViewController.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class EditNicknameAndBadgeViewController: UITableViewController { +final class EditNicknameAndBadgeViewController: UITableViewController { @IBOutlet private weak var nicknameTextField: UITextField! diff --git a/Yep/ViewControllers/EditProfile/EditProfile.storyboard b/Yep/ViewControllers/EditProfile/EditProfile.storyboard index 026907ee4..83bd0f672 100644 --- a/Yep/ViewControllers/EditProfile/EditProfile.storyboard +++ b/Yep/ViewControllers/EditProfile/EditProfile.storyboard @@ -1,8 +1,7 @@ - + - - + @@ -43,41 +42,34 @@ - - - - - - - - - - - + + + + + + + + + - - + - - - + - - @@ -99,10 +91,12 @@ + + @@ -120,7 +114,15 @@ - + + + + + + + + + diff --git a/Yep/ViewControllers/EditProfile/EditProfileViewController.swift b/Yep/ViewControllers/EditProfile/EditProfileViewController.swift index 30158a374..ace9a6149 100644 --- a/Yep/ViewControllers/EditProfile/EditProfileViewController.swift +++ b/Yep/ViewControllers/EditProfile/EditProfileViewController.swift @@ -12,7 +12,7 @@ import TPKeyboardAvoiding import Proposer import Navi -class EditProfileViewController: SegueViewController { +final class EditProfileViewController: SegueViewController { struct Notification { static let Logout = "LogoutNotification" @@ -24,7 +24,17 @@ class EditProfileViewController: SegueViewController { @IBOutlet private weak var activityIndicator: UIActivityIndicatorView! - @IBOutlet private weak var mobileLabel: UILabel! + @IBOutlet private weak var mobileContainerView: UIStackView! { + didSet { + let tap = UITapGestureRecognizer(target: self, action: #selector(EditProfileViewController.tapMobileContainer(_:))) + mobileContainerView.addGestureRecognizer(tap) + } + } + @IBOutlet private weak var mobileLabel: UILabel! { + didSet { + mobileLabel.textColor = UIColor.yepTintColor() + } + } @IBOutlet private weak var editProfileTableView: TPKeyboardAvoidingTableView! @@ -76,7 +86,9 @@ class EditProfileViewController: SegueViewController { updateAvatar() {} - mobileLabel.text = YepUserDefaults.fullPhoneNumber + YepUserDefaults.mobile.bindAndFireListener("") { [weak self] _ in + self?.mobileLabel.text = YepUserDefaults.fullPhoneNumber + } editProfileTableView.registerNib(UINib(nibName: editProfileLessInfoCellIdentifier, bundle: nil), forCellReuseIdentifier: editProfileLessInfoCellIdentifier) editProfileTableView.registerNib(UINib(nibName: editProfileMoreInfoCellIdentifier, bundle: nil), forCellReuseIdentifier: editProfileMoreInfoCellIdentifier) @@ -89,6 +101,11 @@ class EditProfileViewController: SegueViewController { view.endEditing(true) } + // MARK: Unwind + + @IBAction func unwindToEditProfile(segue: UIStoryboardSegue) { + } + // MARK: Actions private func updateAvatar(completion:() -> Void) { @@ -121,7 +138,6 @@ class EditProfileViewController: SegueViewController { if let strongSelf = self { strongSelf.imagePicker.sourceType = .PhotoLibrary strongSelf.presentViewController(strongSelf.imagePicker, animated: true, completion: nil) - } } @@ -165,6 +181,24 @@ class EditProfileViewController: SegueViewController { } } + @objc private func tapMobileContainer(sender: UITapGestureRecognizer) { + + let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet) + + let changeMobileAction: UIAlertAction = UIAlertAction(title: NSLocalizedString("Change Mobile", comment: ""), style: .Default) { [weak self] action in + + self?.performSegueWithIdentifier("showChangeMobile", sender: nil) + } + alertController.addAction(changeMobileAction) + + let cancelAction: UIAlertAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .Cancel) { action -> Void in + self.dismissViewControllerAnimated(true, completion: nil) + } + alertController.addAction(cancelAction) + + self.presentViewController(alertController, animated: true, completion: nil) + } + @objc private func saveIntroduction(sender: UIBarButtonItem) { let introductionCellIndexPath = NSIndexPath(forRow: InfoRow.Intro.rawValue, inSection: Section.Info.rawValue) diff --git a/Yep/ViewControllers/EditSkills/EditSkillsViewController.swift b/Yep/ViewControllers/EditSkills/EditSkillsViewController.swift index 3df6de8ee..faa395469 100644 --- a/Yep/ViewControllers/EditSkills/EditSkillsViewController.swift +++ b/Yep/ViewControllers/EditSkills/EditSkillsViewController.swift @@ -10,7 +10,7 @@ import UIKit import Ruler import RealmSwift -class EditSkillsViewController: BaseViewController { +final class EditSkillsViewController: BaseViewController { var skillSet: SkillSet? var afterChangedSkillsAction: (() -> Void)? diff --git a/Yep/ViewControllers/FeedConversations/FeedConversationsViewController.swift b/Yep/ViewControllers/FeedConversations/FeedConversationsViewController.swift index a9984119d..6c9380fd5 100644 --- a/Yep/ViewControllers/FeedConversations/FeedConversationsViewController.swift +++ b/Yep/ViewControllers/FeedConversations/FeedConversationsViewController.swift @@ -9,7 +9,7 @@ import UIKit import RealmSwift -class FeedConversationsViewController: SegueViewController { +final class FeedConversationsViewController: SegueViewController { @IBOutlet weak var feedConversationsTableView: UITableView! diff --git a/Yep/ViewControllers/Feedback/FeedbackViewController.swift b/Yep/ViewControllers/Feedback/FeedbackViewController.swift index 406c9c97a..f31ab2164 100644 --- a/Yep/ViewControllers/Feedback/FeedbackViewController.swift +++ b/Yep/ViewControllers/Feedback/FeedbackViewController.swift @@ -10,7 +10,7 @@ import UIKit import KeyboardMan import DeviceGuru -class FeedbackViewController: UIViewController { +final class FeedbackViewController: UIViewController { @IBOutlet private weak var promptLabel: UILabel! { didSet { diff --git a/Yep/ViewControllers/Feeds/FeedsMoreViewManager.swift b/Yep/ViewControllers/Feeds/FeedsMoreViewManager.swift index 80df4a927..c3363b1d2 100644 --- a/Yep/ViewControllers/Feeds/FeedsMoreViewManager.swift +++ b/Yep/ViewControllers/Feeds/FeedsMoreViewManager.swift @@ -8,7 +8,7 @@ import Foundation -class FeedsMoreViewManager { +final class FeedsMoreViewManager { var toggleBlockFeedsAction: (() -> Void)? diff --git a/Yep/ViewControllers/Feeds/FeedsViewController.swift b/Yep/ViewControllers/Feeds/FeedsViewController.swift index 118322c6b..4dd66ed09 100644 --- a/Yep/ViewControllers/Feeds/FeedsViewController.swift +++ b/Yep/ViewControllers/Feeds/FeedsViewController.swift @@ -12,7 +12,7 @@ import AVFoundation import MapKit import Ruler -class FeedsViewController: BaseViewController { +final class FeedsViewController: BaseViewController { static let feedNormalImagesCountThreshold: Int = Ruler.UniversalHorizontal(3, 3, 4, 3, 4).value @@ -56,15 +56,14 @@ class FeedsViewController: BaseViewController { private lazy var searchBar: UISearchBar = { let searchBar = UISearchBar() searchBar.searchBarStyle = .Minimal - searchBar.placeholder = NSLocalizedString("Search Feeds", comment: "") searchBar.setSearchFieldBackgroundImage(UIImage(named: "searchbar_textfield_background"), forState: .Normal) searchBar.delegate = self return searchBar }() - private var originalNavigationControllerDelegate: UINavigationControllerDelegate? - private lazy var feedsSearchTransition: FeedsSearchTransition = { - return FeedsSearchTransition() + var originalNavigationControllerDelegate: UINavigationControllerDelegate? + lazy var searchTransition: SearchTransition = { + return SearchTransition() }() private let feedSkillUsersCellID = "FeedSkillUsersCell" @@ -301,10 +300,19 @@ class FeedsViewController: BaseViewController { private var feedCellLayoutHash = [String: FeedCellLayout]() - private func feedCellLayoutOfFeed(feed: DiscoveredFeed) -> FeedCellLayout? { + private mutating func feedCellLayoutOfFeed(feed: DiscoveredFeed) -> FeedCellLayout { let key = feed.id - return feedCellLayoutHash[key] + if let layout = feedCellLayoutHash[key] { + return layout + + } else { + let layout = FeedCellLayout(feed: feed) + + updateFeedCellLayout(layout, forFeed: feed) + + return layout + } } private mutating func updateFeedCellLayout(layout: FeedCellLayout, forFeed feed: DiscoveredFeed) { @@ -320,14 +328,8 @@ class FeedsViewController: BaseViewController { private mutating func heightOfFeed(feed: DiscoveredFeed) -> CGFloat { - if let layout = feedCellLayoutOfFeed(feed) { - return layout.height - - } else { - let layout = FeedCellLayout(feed: feed) - updateFeedCellLayout(layout, forFeed: feed) - return layout.height - } + let layout = feedCellLayoutOfFeed(feed) + return layout.height } } private static var layoutPool = LayoutPool() @@ -374,6 +376,16 @@ class FeedsViewController: BaseViewController { title = NSLocalizedString("Feeds", comment: "") + searchBar.placeholder = NSLocalizedString("Search Feeds", comment: "") + + if skill != nil { + searchBar.placeholder = NSLocalizedString("Search feeds in channel", comment: "") + } + + if profileUser != nil { + searchBar.placeholder = NSLocalizedString("Search feeds by user", comment: "") + } + feedsTableView.separatorColor = UIColor.yepCellSeparatorColor() feedsTableView.contentOffset.y = CGRectGetHeight(searchBar.frame) @@ -428,12 +440,6 @@ class FeedsViewController: BaseViewController { } } } - - if preparedFeedsCount > 0 { - currentPageIndex = 2 - } else { - updateFeeds() - } // 没有 profileUser 才设置 feedSortStyle 以请求服务器 if profileUser == nil { @@ -457,32 +463,27 @@ class FeedsViewController: BaseViewController { } } + if preparedFeedsCount > 0 { + currentPageIndex = 2 + } else { + updateFeeds() + } + #if DEBUG //view.addSubview(feedsFPSLabel) #endif } - override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) - /* - // 尝试恢复原始的 NavigationControllerDelegate,如果自定义 push 了才需要 - if let delegate = originalNavigationControllerDelegate { - navigationController?.delegate = delegate - navigationControllerDelegate = nil - } - */ navigationController?.setNavigationBarHidden(false, animated: false) - - //tabBarController?.tabBar.hidden = (skill == nil && profileUser == nil) ? false : true } - override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() } override func viewWillDisappear(animated: Bool) { @@ -826,12 +827,6 @@ class FeedsViewController: BaseViewController { // MARK: - Navigation - private func recoverNavigationDelegate() { - if let originalNavigationControllerDelegate = originalNavigationControllerDelegate { - navigationController?.delegate = originalNavigationControllerDelegate - } - } - private var newFeedViewController: NewFeedViewController? override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { @@ -900,13 +895,6 @@ class FeedsViewController: BaseViewController { return self } - func hackNavigationDelegate() { - // 在自定义 push 之前,记录原始的 NavigationControllerDelegate 以便 pop 后恢复 - originalNavigationControllerDelegate = navigationController?.delegate - - navigationController?.delegate = feedsSearchTransition - } - switch identifier { case "showSearchFeeds": @@ -914,9 +902,12 @@ class FeedsViewController: BaseViewController { let vc = segue.destinationViewController as! SearchFeedsViewController vc.originalNavigationControllerDelegate = navigationController?.delegate + vc.skill = skill + vc.profileUser = profileUser + vc.hidesBottomBarWhenPushed = true - hackNavigationDelegate() + prepareSearchTransition() case "showProfile": @@ -943,7 +934,7 @@ class FeedsViewController: BaseViewController { vc.hidesBottomBarWhenPushed = true - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() case "showSkillHome": @@ -955,7 +946,7 @@ class FeedsViewController: BaseViewController { vc.hidesBottomBarWhenPushed = true - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() case "showFeedsWithSkill": @@ -977,7 +968,7 @@ class FeedsViewController: BaseViewController { vc.hidesBottomBarWhenPushed = true - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() case "showConversation": @@ -1066,7 +1057,7 @@ class FeedsViewController: BaseViewController { strongSelf.feedAudioPlaybackTimer = NSTimer.scheduledTimerWithTimeInterval(0.02, target: strongSelf, selector: #selector(FeedsViewController.updateOnlineAudioPlaybackProgress(_:)), userInfo: nil, repeats: true) } - recoverNavigationDelegate() + recoverOriginalNavigationDelegate() case "presentNewFeed": @@ -1083,6 +1074,8 @@ class FeedsViewController: BaseViewController { vc.afterCreatedFeedAction = afterCreatedFeedAction vc.getFeedsViewController = getFeedsViewController + recoverOriginalNavigationDelegate() + case "presentNewFeedVoiceRecord": guard let @@ -1098,6 +1091,8 @@ class FeedsViewController: BaseViewController { vc.afterCreatedFeedAction = afterCreatedFeedAction vc.getFeedsViewController = getFeedsViewController + recoverOriginalNavigationDelegate() + case "presentPickLocation": guard let @@ -1113,6 +1108,8 @@ class FeedsViewController: BaseViewController { vc.afterCreatedFeedAction = afterCreatedFeedAction + recoverOriginalNavigationDelegate() + default: break } @@ -1295,16 +1292,12 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { } let layout = FeedsViewController.layoutPool.feedCellLayoutOfFeed(feed) - let update: FeedCellLayout.Update = { newLayout in - FeedsViewController.layoutPool.updateFeedCellLayout(newLayout, forFeed: feed) - } - let layoutCache = (layout: layout, update: update) switch feed.kind { case .Text: - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) case .URL: @@ -1312,7 +1305,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapURLInfoAction = { [weak self] URL in println("tapURLInfoAction URL: \(URL)") @@ -1357,7 +1350,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapMediaAction = tapMediaAction @@ -1367,7 +1360,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapMediaAction = tapMediaAction @@ -1376,7 +1369,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapMediaAction = tapMediaAction } @@ -1387,7 +1380,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapGithubRepoLinkAction = { [weak self] URL in self?.yep_openURL(URL) @@ -1399,7 +1392,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapDribbbleShotLinkAction = { [weak self] URL in self?.yep_openURL(URL) @@ -1440,7 +1433,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) /* cell.playOrPauseAudioAction = { [weak self] cell in @@ -1578,7 +1571,7 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { break } - cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill) + cell.configureWithFeed(feed, layout: layout, needShowSkill: needShowSkill) cell.tapLocationAction = { locationName, locationCoordinate in diff --git a/Yep/ViewControllers/FriendsInContacts/FriendsInContacts.storyboard b/Yep/ViewControllers/FriendsInContacts/FriendsInContacts.storyboard index e442a9202..a6b3520b6 100644 --- a/Yep/ViewControllers/FriendsInContacts/FriendsInContacts.storyboard +++ b/Yep/ViewControllers/FriendsInContacts/FriendsInContacts.storyboard @@ -1,8 +1,7 @@ - + - - + diff --git a/Yep/ViewControllers/FriendsInContacts/FriendsInContactsViewController.swift b/Yep/ViewControllers/FriendsInContacts/FriendsInContactsViewController.swift index 6fa3d7dbb..6a08a5ae8 100644 --- a/Yep/ViewControllers/FriendsInContacts/FriendsInContactsViewController.swift +++ b/Yep/ViewControllers/FriendsInContacts/FriendsInContactsViewController.swift @@ -9,7 +9,7 @@ import UIKit import Contacts -class FriendsInContactsViewController: BaseViewController { +final class FriendsInContactsViewController: BaseViewController { struct Notification { static let NewFriends = "NewFriendsInContactsNotification" diff --git a/Yep/ViewControllers/Login/LoginByMobileViewController.swift b/Yep/ViewControllers/Login/LoginByMobileViewController.swift index e10ef10c6..9365d8df5 100644 --- a/Yep/ViewControllers/Login/LoginByMobileViewController.swift +++ b/Yep/ViewControllers/Login/LoginByMobileViewController.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class LoginByMobileViewController: BaseViewController { +final class LoginByMobileViewController: BaseViewController { @IBOutlet private weak var pickMobileNumberPromptLabel: UILabel! @IBOutlet private weak var pickMobileNumberPromptLabelTopConstraint: NSLayoutConstraint! diff --git a/Yep/ViewControllers/Login/LoginVerifyMobileViewController.swift b/Yep/ViewControllers/Login/LoginVerifyMobileViewController.swift index 0c1e0729c..c6881e043 100644 --- a/Yep/ViewControllers/Login/LoginVerifyMobileViewController.swift +++ b/Yep/ViewControllers/Login/LoginVerifyMobileViewController.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class LoginVerifyMobileViewController: UIViewController { +final class LoginVerifyMobileViewController: UIViewController { var mobile: String! var areaCode: String! diff --git a/Yep/ViewControllers/MediaPreview/MediaPreviewViewController.swift b/Yep/ViewControllers/MediaPreview/MediaPreviewViewController.swift index af3d4a2be..b61116155 100644 --- a/Yep/ViewControllers/MediaPreview/MediaPreviewViewController.swift +++ b/Yep/ViewControllers/MediaPreview/MediaPreviewViewController.swift @@ -21,7 +21,7 @@ enum PreviewMedia { case WebImage(imageURL: NSURL, linkURL: NSURL) } -class MediaPreviewViewController: UIViewController { +final class MediaPreviewViewController: UIViewController { var previewMedias: [PreviewMedia] = [] var startIndex: Int = 0 diff --git a/Yep/ViewControllers/Nav/YepNavigationController.swift b/Yep/ViewControllers/Nav/YepNavigationController.swift index d25dc5a05..e06b5d8f8 100644 --- a/Yep/ViewControllers/Nav/YepNavigationController.swift +++ b/Yep/ViewControllers/Nav/YepNavigationController.swift @@ -8,7 +8,7 @@ import UIKit -class YepNavigationController: UINavigationController, UIGestureRecognizerDelegate, UINavigationControllerDelegate { +final class YepNavigationController: UINavigationController, UIGestureRecognizerDelegate, UINavigationControllerDelegate { override func viewDidLoad() { super.viewDidLoad() diff --git a/Yep/ViewControllers/NewFeed/NewFeedPreviewViewController.swift b/Yep/ViewControllers/NewFeed/NewFeedPreviewViewController.swift index 7e830ae8c..4d9e76982 100644 --- a/Yep/ViewControllers/NewFeed/NewFeedPreviewViewController.swift +++ b/Yep/ViewControllers/NewFeed/NewFeedPreviewViewController.swift @@ -11,7 +11,7 @@ import UIKit private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width private let screenHeight: CGFloat = UIScreen.mainScreen().bounds.height -class NewFeedPreviewViewController: UIViewController { +final class NewFeedPreviewViewController: UIViewController { @IBOutlet weak var previewCollectionView: UICollectionView! diff --git a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift index f6015375f..a8ff520c9 100644 --- a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift +++ b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift @@ -24,7 +24,7 @@ struct FeedVoice { let limitedSampleValues: [CGFloat] } -class NewFeedViewController: SegueViewController { +final class NewFeedViewController: SegueViewController { enum Attachment { case Default @@ -623,7 +623,7 @@ class NewFeedViewController: SegueViewController { break } - return DiscoveredFeed(id: "", allowComment: true, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: message, attachment: feedAttachment, distance: 0, skill: pickedSkill, groupID: "", messagesCount: 0, uploadingErrorMessage: nil) + return DiscoveredFeed(id: "", allowComment: true, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: message, highlightedKeywordsBody: nil, attachment: feedAttachment, distance: 0, skill: pickedSkill, groupID: "", messagesCount: 0, uploadingErrorMessage: nil) } @objc private func post(sender: UIBarButtonItem) { diff --git a/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift b/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift index 1fc9e88a3..9347c8da6 100644 --- a/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift +++ b/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift @@ -9,7 +9,7 @@ import UIKit import AVFoundation -class NewFeedVoiceRecordViewController: SegueViewController { +final class NewFeedVoiceRecordViewController: SegueViewController { var preparedSkill: Skill? diff --git a/Yep/ViewControllers/Notifications/NotificationsViewController.swift b/Yep/ViewControllers/Notifications/NotificationsViewController.swift index 141cea35f..8532e449d 100644 --- a/Yep/ViewControllers/Notifications/NotificationsViewController.swift +++ b/Yep/ViewControllers/Notifications/NotificationsViewController.swift @@ -62,7 +62,7 @@ struct DoNotDisturbPeriod { } } -class NotificationsViewController: SegueViewController { +final class NotificationsViewController: SegueViewController { @IBOutlet private weak var tableView: UITableView! diff --git a/Yep/ViewControllers/OAuth/OAuthViewController.swift b/Yep/ViewControllers/OAuth/OAuthViewController.swift index 928f6d49e..a674697be 100644 --- a/Yep/ViewControllers/OAuth/OAuthViewController.swift +++ b/Yep/ViewControllers/OAuth/OAuthViewController.swift @@ -9,7 +9,7 @@ import UIKit import OnePasswordExtension -class OAuthViewController: BaseViewController { +final class OAuthViewController: BaseViewController { var socialAccount: SocialAccount! var afterOAuthAction: ((socialAccount: SocialAccount) -> Void)? diff --git a/Yep/ViewControllers/PickLocation/LocationPin.swift b/Yep/ViewControllers/PickLocation/LocationPin.swift index 56447a8e3..c970ddd56 100644 --- a/Yep/ViewControllers/PickLocation/LocationPin.swift +++ b/Yep/ViewControllers/PickLocation/LocationPin.swift @@ -8,7 +8,7 @@ import MapKit -class LocationPin: NSObject, MKAnnotation { +final class LocationPin: NSObject, MKAnnotation { let title: String? let subtitle: String? let coordinate: CLLocationCoordinate2D diff --git a/Yep/ViewControllers/PickLocation/PickLocationViewController.swift b/Yep/ViewControllers/PickLocation/PickLocationViewController.swift index 3ee5cfbab..56616230d 100644 --- a/Yep/ViewControllers/PickLocation/PickLocationViewController.swift +++ b/Yep/ViewControllers/PickLocation/PickLocationViewController.swift @@ -10,7 +10,7 @@ import UIKit import MapKit import Proposer -class PickLocationViewController: SegueViewController { +final class PickLocationViewController: SegueViewController { enum Purpose { case Message diff --git a/Yep/ViewControllers/PickPhotos/ImageCacheController.swift b/Yep/ViewControllers/PickPhotos/ImageCacheController.swift index a89809c58..3f333aea3 100644 --- a/Yep/ViewControllers/PickPhotos/ImageCacheController.swift +++ b/Yep/ViewControllers/PickPhotos/ImageCacheController.swift @@ -9,7 +9,7 @@ import Foundation import Photos -class ImageCacheController { +final class ImageCacheController { private var cachedIndices = NSIndexSet() var cachePreheatSize: Int diff --git a/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift b/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift index d03aa5d6c..38faf4668 100644 --- a/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift +++ b/Yep/ViewControllers/PickPhotos/PickPhotosViewController.swift @@ -14,11 +14,14 @@ protocol ReturnPickedPhotosDelegate: class { func returnSelectedImages(images: [UIImage], imageAssets: [PHAsset]) } -class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChangeObserver { +final class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChangeObserver { var images: PHFetchResult? { didSet { collectionView?.reloadData() + guard let images = images, collectionView = collectionView else { return } + + collectionView.scrollToItemAtIndexPath(NSIndexPath(forItem: images.count - 1, inSection: 0), atScrollPosition: .CenteredVertically, animated: false) } } var imagesDidFetch: Bool = false @@ -41,8 +44,8 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange collectionView?.backgroundColor = UIColor.whiteColor() collectionView?.alwaysBounceVertical = true + automaticallyAdjustsScrollViewInsets = false collectionView?.registerNib(UINib(nibName: photoCellID, bundle: nil), forCellWithReuseIdentifier: photoCellID) - if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout { let width: CGFloat = Ruler.iPhoneVertical(77.5, 77.5, 92.5, 102).value @@ -52,7 +55,7 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange let gap: CGFloat = Ruler.iPhoneHorizontal(1, 1, 1).value layout.minimumInteritemSpacing = gap layout.minimumLineSpacing = gap - layout.sectionInset = UIEdgeInsets(top: gap, left: gap, bottom: gap, right: gap) + layout.sectionInset = UIEdgeInsets(top: gap + 64, left: gap, bottom: gap, right: gap) } let backBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon_back"), style: UIBarButtonItemStyle.Plain, target: self, action: #selector(PickPhotosViewController.back(_:))) @@ -64,7 +67,7 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange if !imagesDidFetch { let options = PHFetchOptions() options.sortDescriptors = [ - NSSortDescriptor(key: "creationDate", ascending: false) + NSSortDescriptor(key: "creationDate", ascending: true) ] images = PHAsset.fetchAssetsWithMediaType(.Image, options: options) } @@ -85,16 +88,18 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange navigationController?.interactivePopGestureRecognizer?.delegate = self } - + + override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) - + guard let images = images else { return } + imageCacheController = ImageCacheController(imageManager: imageManager, images: images, preheatSize: 1) navigationController?.interactivePopGestureRecognizer?.enabled = true } - + // MARK: Actions func back(sender: UIBarButtonItem) { @@ -262,4 +267,5 @@ extension PickPhotosViewController: UIGestureRecognizerDelegate { } return false } + } \ No newline at end of file diff --git a/Yep/ViewControllers/PodsHelpYep/PodsHelpYep.storyboard b/Yep/ViewControllers/PodsHelpYep/PodsHelpYep.storyboard index 2d12b9b55..c325d2dfd 100644 --- a/Yep/ViewControllers/PodsHelpYep/PodsHelpYep.storyboard +++ b/Yep/ViewControllers/PodsHelpYep/PodsHelpYep.storyboard @@ -1,8 +1,7 @@ - + - - + @@ -14,15 +13,39 @@ - + + + + + + + + + + + + + - + - diff --git a/Yep/Views/Cells/FeedConversationDock/FeedConversationDockCell.swift b/Yep/Views/Cells/FeedConversationDock/FeedConversationDockCell.swift index 4a3a85ad5..2950ee2cf 100644 --- a/Yep/Views/Cells/FeedConversationDock/FeedConversationDockCell.swift +++ b/Yep/Views/Cells/FeedConversationDock/FeedConversationDockCell.swift @@ -8,7 +8,7 @@ import UIKit -class FeedConversationDockCell: UITableViewCell { +final class FeedConversationDockCell: UITableViewCell { @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var nameLabel: UILabel! diff --git a/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift b/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift index 71b624a96..d6ffaa6d6 100644 --- a/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift +++ b/Yep/Views/Cells/FeedMedia/FeedMediaCell.swift @@ -8,7 +8,7 @@ import UIKit -class FeedMediaCell: UICollectionViewCell { +final class FeedMediaCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var deleteImageView: UIImageView! diff --git a/Yep/Views/Cells/FeedMediaAdd/FeedMediaAddCell.swift b/Yep/Views/Cells/FeedMediaAdd/FeedMediaAddCell.swift index 1a416952c..ed99f09f1 100644 --- a/Yep/Views/Cells/FeedMediaAdd/FeedMediaAddCell.swift +++ b/Yep/Views/Cells/FeedMediaAdd/FeedMediaAddCell.swift @@ -8,7 +8,7 @@ import UIKit -class FeedMediaAddCell: UICollectionViewCell { +final class FeedMediaAddCell: UICollectionViewCell { @IBOutlet weak var mediaAddImage: UIImageView! diff --git a/Yep/Views/Cells/FeedSkillUsers/FeedSkillUsersCell.swift b/Yep/Views/Cells/FeedSkillUsers/FeedSkillUsersCell.swift index 2c255e5e8..6eee3fe53 100644 --- a/Yep/Views/Cells/FeedSkillUsers/FeedSkillUsersCell.swift +++ b/Yep/Views/Cells/FeedSkillUsers/FeedSkillUsersCell.swift @@ -8,7 +8,7 @@ import UIKit -class FeedSkillUsersCell: UITableViewCell { +final class FeedSkillUsersCell: UITableViewCell { @IBOutlet weak var promptLabel: UILabel! diff --git a/Yep/Views/Cells/GithubRepo/GithubRepoCell.swift b/Yep/Views/Cells/GithubRepo/GithubRepoCell.swift index 7d321fa35..2d414bc03 100644 --- a/Yep/Views/Cells/GithubRepo/GithubRepoCell.swift +++ b/Yep/Views/Cells/GithubRepo/GithubRepoCell.swift @@ -8,7 +8,7 @@ import UIKit -class GithubRepoCell: UITableViewCell { +final class GithubRepoCell: UITableViewCell { @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var nameLabelLeadingConstraint: NSLayoutConstraint! diff --git a/Yep/Views/Cells/InstagramMedia/InstagramMediaCell.swift b/Yep/Views/Cells/InstagramMedia/InstagramMediaCell.swift index f1425fa4b..e45b4411d 100644 --- a/Yep/Views/Cells/InstagramMedia/InstagramMediaCell.swift +++ b/Yep/Views/Cells/InstagramMedia/InstagramMediaCell.swift @@ -9,7 +9,7 @@ import UIKit import Kingfisher -class InstagramMediaCell: UICollectionViewCell { +final class InstagramMediaCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! diff --git a/Yep/Views/Cells/LoadMore/LoadMoreCollectionViewCell.swift b/Yep/Views/Cells/LoadMore/LoadMoreCollectionViewCell.swift index cf873eb25..b1a5401ad 100644 --- a/Yep/Views/Cells/LoadMore/LoadMoreCollectionViewCell.swift +++ b/Yep/Views/Cells/LoadMore/LoadMoreCollectionViewCell.swift @@ -8,7 +8,7 @@ import UIKit -class LoadMoreCollectionViewCell: UICollectionViewCell { +final class LoadMoreCollectionViewCell: UICollectionViewCell { @IBOutlet weak var loadingActivityIndicator: UIActivityIndicatorView! diff --git a/Yep/Views/Cells/LoadMore/LoadMoreTableViewCell.swift b/Yep/Views/Cells/LoadMore/LoadMoreTableViewCell.swift index 6404a4037..54ad0e791 100644 --- a/Yep/Views/Cells/LoadMore/LoadMoreTableViewCell.swift +++ b/Yep/Views/Cells/LoadMore/LoadMoreTableViewCell.swift @@ -8,7 +8,7 @@ import UIKit -class LoadMoreTableViewCell: UITableViewCell { +final class LoadMoreTableViewCell: UITableViewCell { var isLoading: Bool = false { didSet { diff --git a/Yep/Views/Cells/MediaView/MediaViewCell.swift b/Yep/Views/Cells/MediaView/MediaViewCell.swift index 8bb9cb242..cc9ea4088 100644 --- a/Yep/Views/Cells/MediaView/MediaViewCell.swift +++ b/Yep/Views/Cells/MediaView/MediaViewCell.swift @@ -8,7 +8,7 @@ import UIKit -class MediaViewCell: UICollectionViewCell { +final class MediaViewCell: UICollectionViewCell { @IBOutlet weak var mediaView: MediaView! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! diff --git a/Yep/Views/Cells/NewFeedPreview/AlbumListCell.swift b/Yep/Views/Cells/NewFeedPreview/AlbumListCell.swift index e982d3ff9..8aec5f2be 100644 --- a/Yep/Views/Cells/NewFeedPreview/AlbumListCell.swift +++ b/Yep/Views/Cells/NewFeedPreview/AlbumListCell.swift @@ -8,7 +8,7 @@ import UIKit -class AlbumListCell: UITableViewCell { +final class AlbumListCell: UITableViewCell { @IBOutlet weak var countLabel: UILabel! @IBOutlet weak var titleLabel: UILabel! diff --git a/Yep/Views/Cells/NewFeedPreview/NewFeedPreviewCell.swift b/Yep/Views/Cells/NewFeedPreview/NewFeedPreviewCell.swift index cf58b61ae..c24d542cf 100644 --- a/Yep/Views/Cells/NewFeedPreview/NewFeedPreviewCell.swift +++ b/Yep/Views/Cells/NewFeedPreview/NewFeedPreviewCell.swift @@ -8,7 +8,7 @@ import UIKit -class NewFeedPreviewCell: UICollectionViewCell { +final class NewFeedPreviewCell: UICollectionViewCell { @IBOutlet weak var image: UIImageView! diff --git a/Yep/Views/Cells/Photo/PhotoCell.swift b/Yep/Views/Cells/Photo/PhotoCell.swift index b5fae5da7..149ce0154 100644 --- a/Yep/Views/Cells/Photo/PhotoCell.swift +++ b/Yep/Views/Cells/Photo/PhotoCell.swift @@ -9,7 +9,7 @@ import UIKit import Photos -class PhotoCell: UICollectionViewCell { +final class PhotoCell: UICollectionViewCell { @IBOutlet weak var photoImageView: UIImageView! @IBOutlet weak var photoPickedImageView: UIImageView! diff --git a/Yep/Views/Cells/PickLocation/PickLocationCell.swift b/Yep/Views/Cells/PickLocation/PickLocationCell.swift index abd5d8911..60338c20f 100644 --- a/Yep/Views/Cells/PickLocation/PickLocationCell.swift +++ b/Yep/Views/Cells/PickLocation/PickLocationCell.swift @@ -8,7 +8,7 @@ import UIKit -class PickLocationCell: UITableViewCell { +final class PickLocationCell: UITableViewCell { @IBOutlet weak var iconImageView: UIImageView! diff --git a/Yep/Views/Cells/ProfileFeeds/ProfileFeedsCell.swift b/Yep/Views/Cells/ProfileFeeds/ProfileFeedsCell.swift index 804f9f876..179880379 100644 --- a/Yep/Views/Cells/ProfileFeeds/ProfileFeedsCell.swift +++ b/Yep/Views/Cells/ProfileFeeds/ProfileFeedsCell.swift @@ -8,7 +8,7 @@ import UIKit -class ProfileFeedsCell: UICollectionViewCell { +final class ProfileFeedsCell: UICollectionViewCell { @IBOutlet weak var iconImageView: UIImageView! @IBOutlet weak var iconImageViewLeadingConstraint: NSLayoutConstraint! diff --git a/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.swift b/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.swift index 2d070bf67..8d4e01a46 100644 --- a/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.swift +++ b/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.swift @@ -10,7 +10,7 @@ import UIKit import CoreLocation import RealmSwift -class ProfileFooterCell: UICollectionViewCell { +final class ProfileFooterCell: UICollectionViewCell { @IBOutlet weak var nicknameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel! diff --git a/Yep/Views/Cells/ProfileHeader/ProfileHeaderCell.swift b/Yep/Views/Cells/ProfileHeader/ProfileHeaderCell.swift index 2eefe39fa..4842916d6 100644 --- a/Yep/Views/Cells/ProfileHeader/ProfileHeaderCell.swift +++ b/Yep/Views/Cells/ProfileHeader/ProfileHeaderCell.swift @@ -12,7 +12,7 @@ import FXBlurView import Proposer import Navi -class ProfileHeaderCell: UICollectionViewCell { +final class ProfileHeaderCell: UICollectionViewCell { @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarBlurImageView: UIImageView! diff --git a/Yep/Views/Cells/ProfileSeparationLine/ProfileSeparationLineCell.swift b/Yep/Views/Cells/ProfileSeparationLine/ProfileSeparationLineCell.swift index aff9dfa72..cf60d7e68 100644 --- a/Yep/Views/Cells/ProfileSeparationLine/ProfileSeparationLineCell.swift +++ b/Yep/Views/Cells/ProfileSeparationLine/ProfileSeparationLineCell.swift @@ -8,7 +8,7 @@ import UIKit -class ProfileSeparationLineCell: UICollectionViewCell { +final class ProfileSeparationLineCell: UICollectionViewCell { var leftEdgeInset: CGFloat = YepConfig.Profile.leftEdgeInset var rightEdgeInset: CGFloat = YepConfig.Profile.rightEdgeInset diff --git a/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountCell.swift b/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountCell.swift index 95e3dd2e5..d6d6f593b 100644 --- a/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountCell.swift +++ b/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountCell.swift @@ -8,7 +8,7 @@ import UIKit -class ProfileSocialAccountCell: UICollectionViewCell { +final class ProfileSocialAccountCell: UICollectionViewCell { @IBOutlet weak var iconImageView: UIImageView! @IBOutlet weak var iconImageViewLeadingConstraint: NSLayoutConstraint! diff --git a/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountGithubCell.swift b/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountGithubCell.swift index 6f3ef321d..98e16789d 100644 --- a/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountGithubCell.swift +++ b/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountGithubCell.swift @@ -8,7 +8,7 @@ import UIKit -class ProfileSocialAccountGithubCell: UICollectionViewCell { +final class ProfileSocialAccountGithubCell: UICollectionViewCell { var githubWork: GithubWork? { didSet { diff --git a/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountImagesCell.swift b/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountImagesCell.swift index 1dc5f313b..5a5ee4b18 100644 --- a/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountImagesCell.swift +++ b/Yep/Views/Cells/ProfileSocialAccount/ProfileSocialAccountImagesCell.swift @@ -9,7 +9,7 @@ import UIKit import Kingfisher -class ProfileSocialAccountImagesCell: UICollectionViewCell { +final class ProfileSocialAccountImagesCell: UICollectionViewCell { var socialAccount: SocialAccount? diff --git a/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift b/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift index 00ab2d468..8ea895fe7 100644 --- a/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift +++ b/Yep/Views/Cells/QuickPickPhotos/QuickPickPhotosCell.swift @@ -10,7 +10,7 @@ import UIKit import Photos import Proposer -class QuickPickPhotosCell: UITableViewCell { +final class QuickPickPhotosCell: UITableViewCell { @IBOutlet weak var photosCollectionView: UICollectionView! diff --git a/Yep/Views/Cells/SearchMoreResults/SearchMoreResultsCell.swift b/Yep/Views/Cells/SearchMoreResults/SearchMoreResultsCell.swift index b49b6c56b..3bf8cd2c7 100644 --- a/Yep/Views/Cells/SearchMoreResults/SearchMoreResultsCell.swift +++ b/Yep/Views/Cells/SearchMoreResults/SearchMoreResultsCell.swift @@ -8,7 +8,7 @@ import UIKit -class SearchMoreResultsCell: UITableViewCell { +final class SearchMoreResultsCell: UITableViewCell { var fold: Bool = true { didSet { diff --git a/Yep/Views/Cells/SearchSectionTitle/SearchSectionTitleCell.swift b/Yep/Views/Cells/SearchSectionTitle/SearchSectionTitleCell.swift index 11c6dd342..15dce08a0 100644 --- a/Yep/Views/Cells/SearchSectionTitle/SearchSectionTitleCell.swift +++ b/Yep/Views/Cells/SearchSectionTitle/SearchSectionTitleCell.swift @@ -8,7 +8,7 @@ import UIKit -class SearchSectionTitleCell: UITableViewCell { +final class SearchSectionTitleCell: UITableViewCell { @IBOutlet weak var sectionTitleLabel: UILabel! { didSet { diff --git a/Yep/Views/Cells/SearchedDiscoveredUser/SearchedDiscoveredUserCell.swift b/Yep/Views/Cells/SearchedDiscoveredUser/SearchedDiscoveredUserCell.swift index fc7104a1d..569bc061b 100644 --- a/Yep/Views/Cells/SearchedDiscoveredUser/SearchedDiscoveredUserCell.swift +++ b/Yep/Views/Cells/SearchedDiscoveredUser/SearchedDiscoveredUserCell.swift @@ -8,7 +8,7 @@ import UIKit -class SearchedDiscoveredUserCell: UITableViewCell { +final class SearchedDiscoveredUserCell: UITableViewCell { @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var nicknameLabel: UILabel! diff --git a/Yep/Views/Cells/SearchedFeed/SearchedFeedCell.swift b/Yep/Views/Cells/SearchedFeed/SearchedFeedCell.swift index 3c4662f64..05e80a235 100644 --- a/Yep/Views/Cells/SearchedFeed/SearchedFeedCell.swift +++ b/Yep/Views/Cells/SearchedFeed/SearchedFeedCell.swift @@ -8,7 +8,7 @@ import UIKit -class SearchedFeedCell: UITableViewCell { +final class SearchedFeedCell: UITableViewCell { @IBOutlet weak var mediaView: FeedMediaView! @IBOutlet weak var nameLabel: UILabel! diff --git a/Yep/Views/Cells/SearchedMessage/SearchedMessageCell.swift b/Yep/Views/Cells/SearchedMessage/SearchedMessageCell.swift index 7690c10f0..7381610e6 100644 --- a/Yep/Views/Cells/SearchedMessage/SearchedMessageCell.swift +++ b/Yep/Views/Cells/SearchedMessage/SearchedMessageCell.swift @@ -8,7 +8,7 @@ import UIKit -class SearchedMessageCell: UITableViewCell { +final class SearchedMessageCell: UITableViewCell { @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var nicknameLabel: UILabel! diff --git a/Yep/Views/Cells/SearchedUser/SearchedUserCell.swift b/Yep/Views/Cells/SearchedUser/SearchedUserCell.swift index d1d40b930..eae6670e3 100644 --- a/Yep/Views/Cells/SearchedUser/SearchedUserCell.swift +++ b/Yep/Views/Cells/SearchedUser/SearchedUserCell.swift @@ -8,7 +8,7 @@ import UIKit -class SearchedUserCell: UITableViewCell { +final class SearchedUserCell: UITableViewCell { @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var nicknameLabel: UILabel! diff --git a/Yep/Views/Cells/SettingsMore/SettingsMoreCell.swift b/Yep/Views/Cells/SettingsMore/SettingsMoreCell.swift index bd1b3f643..6eaa071e3 100644 --- a/Yep/Views/Cells/SettingsMore/SettingsMoreCell.swift +++ b/Yep/Views/Cells/SettingsMore/SettingsMoreCell.swift @@ -8,7 +8,7 @@ import UIKit -class SettingsMoreCell: UITableViewCell { +final class SettingsMoreCell: UITableViewCell { @IBOutlet weak var annotationLabel: UILabel! diff --git a/Yep/Views/Cells/SettingsUser/SettingsUserCell.swift b/Yep/Views/Cells/SettingsUser/SettingsUserCell.swift index 25299cf59..932c10f82 100644 --- a/Yep/Views/Cells/SettingsUser/SettingsUserCell.swift +++ b/Yep/Views/Cells/SettingsUser/SettingsUserCell.swift @@ -9,7 +9,7 @@ import UIKit import Navi -class SettingsUserCell: UITableViewCell { +final class SettingsUserCell: UITableViewCell { @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageViewWidthConstraint: NSLayoutConstraint! diff --git a/Yep/Views/Cells/Skill/SkillCell.swift b/Yep/Views/Cells/Skill/SkillCell.swift index 8f7ff94dd..f3f4b8ba3 100644 --- a/Yep/Views/Cells/Skill/SkillCell.swift +++ b/Yep/Views/Cells/Skill/SkillCell.swift @@ -8,7 +8,7 @@ import UIKit -class SkillCell: UICollectionViewCell { +final class SkillCell: UICollectionViewCell { static let height: CGFloat = 24 diff --git a/Yep/Views/Cells/SkillAdd/SkillAddCell.swift b/Yep/Views/Cells/SkillAdd/SkillAddCell.swift index 773bf375a..96206ce13 100644 --- a/Yep/Views/Cells/SkillAdd/SkillAddCell.swift +++ b/Yep/Views/Cells/SkillAdd/SkillAddCell.swift @@ -8,7 +8,7 @@ import UIKit -class SkillAddCell: UICollectionViewCell { +final class SkillAddCell: UICollectionViewCell { var skillSet: SkillSet = .Master diff --git a/Yep/Views/Cells/SkillCategory/SkillCategoryCell.swift b/Yep/Views/Cells/SkillCategory/SkillCategoryCell.swift index 8f6501886..986711485 100644 --- a/Yep/Views/Cells/SkillCategory/SkillCategoryCell.swift +++ b/Yep/Views/Cells/SkillCategory/SkillCategoryCell.swift @@ -8,7 +8,7 @@ import UIKit -class SkillCategoryCell: UICollectionViewCell { +final class SkillCategoryCell: UICollectionViewCell { static let skillCategoryButtonWidth: CGFloat = 280//CGRectGetWidth(UIScreen.mainScreen().bounds) - 20 * 2 static let skillCategoryButtonHeight: CGFloat = 60 diff --git a/Yep/Views/Cells/SkillRank/SkillRankCell.swift b/Yep/Views/Cells/SkillRank/SkillRankCell.swift index 20fa701e4..211ae4846 100644 --- a/Yep/Views/Cells/SkillRank/SkillRankCell.swift +++ b/Yep/Views/Cells/SkillRank/SkillRankCell.swift @@ -8,7 +8,7 @@ import UIKit -class SkillRankCell: UICollectionViewCell { +final class SkillRankCell: UICollectionViewCell { @IBOutlet weak var skillLabel: UILabel! @IBOutlet weak var rankView: RankView! diff --git a/Yep/Views/Cells/SkillSelection/SkillSelectionCell.swift b/Yep/Views/Cells/SkillSelection/SkillSelectionCell.swift index 411e45925..1b5bc7b3c 100644 --- a/Yep/Views/Cells/SkillSelection/SkillSelectionCell.swift +++ b/Yep/Views/Cells/SkillSelection/SkillSelectionCell.swift @@ -8,7 +8,7 @@ import UIKit -class SkillSelectionCell: UICollectionViewCell { +final class SkillSelectionCell: UICollectionViewCell { static let height: CGFloat = 35 diff --git a/Yep/Views/Cells/Title/TitleCell.swift b/Yep/Views/Cells/Title/TitleCell.swift index cd2a545ac..70a0eeb8d 100644 --- a/Yep/Views/Cells/Title/TitleCell.swift +++ b/Yep/Views/Cells/Title/TitleCell.swift @@ -8,7 +8,7 @@ import UIKit -class TitleCell: UITableViewCell { +final class TitleCell: UITableViewCell { @IBOutlet weak var singleTitleLabel: UILabel! diff --git a/Yep/Views/Cells/UserState/UserStateCell.swift b/Yep/Views/Cells/UserState/UserStateCell.swift index 34cfbde17..ac7b34abc 100644 --- a/Yep/Views/Cells/UserState/UserStateCell.swift +++ b/Yep/Views/Cells/UserState/UserStateCell.swift @@ -8,7 +8,7 @@ import UIKit -class UserStateCell: UITableViewCell { +final class UserStateCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() diff --git a/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift b/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift index 47f4c6391..f5f017afe 100644 --- a/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift +++ b/Yep/Views/ContainerViews/FeedGithubRepoContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedGithubRepoContainerView: UIView { +final class FeedGithubRepoContainerView: UIView { var tapAction: (() -> Void)? @@ -74,7 +74,7 @@ class FeedGithubRepoContainerView: UIView { descriptionLabel.translatesAutoresizingMaskIntoConstraints = false accessoryImageView.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "backgroundImageView": backgroundImageView, "iconImageView": iconImageView, "nameLabel": nameLabel, diff --git a/Yep/Views/ContainerViews/FeedLocationContainerView.swift b/Yep/Views/ContainerViews/FeedLocationContainerView.swift index e64ff0a97..b5c917a52 100644 --- a/Yep/Views/ContainerViews/FeedLocationContainerView.swift +++ b/Yep/Views/ContainerViews/FeedLocationContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedLocationContainerView: UIView { +final class FeedLocationContainerView: UIView { var tapAction: (() -> Void)? @@ -68,7 +68,7 @@ class FeedLocationContainerView: UIView { horizontalLineView.translatesAutoresizingMaskIntoConstraints = false nameLabel.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "backgroundImageView": backgroundImageView, "mapImageView": mapImageView, "horizontalLineView": horizontalLineView, diff --git a/Yep/Views/ContainerViews/FeedMediaContainerView.swift b/Yep/Views/ContainerViews/FeedMediaContainerView.swift index 51dce1b99..08bff70ff 100644 --- a/Yep/Views/ContainerViews/FeedMediaContainerView.swift +++ b/Yep/Views/ContainerViews/FeedMediaContainerView.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class FeedMediaContainerView: UIView { +final class FeedMediaContainerView: UIView { var tapMediaAction: ((mediaImageView: UIImageView) -> Void)? @@ -60,7 +60,7 @@ class FeedMediaContainerView: UIView { horizontalLineView.translatesAutoresizingMaskIntoConstraints = false linkContainerView.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "backgroundImageView": backgroundImageView, "mediaImageView": mediaImageView, "horizontalLineView": horizontalLineView, diff --git a/Yep/Views/ContainerViews/FeedURLContainerView.swift b/Yep/Views/ContainerViews/FeedURLContainerView.swift index 9c9f70bec..ffe14dc49 100644 --- a/Yep/Views/ContainerViews/FeedURLContainerView.swift +++ b/Yep/Views/ContainerViews/FeedURLContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedURLContainerView: UIView { +final class FeedURLContainerView: UIView { var tapAction: (() -> Void)? @@ -93,7 +93,7 @@ class FeedURLContainerView: UIView { descriptionLabel.translatesAutoresizingMaskIntoConstraints = false thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "backgroundImageView": backgroundImageView, "siteNameLabel": siteNameLabel, "titleLabel": titleLabel, @@ -114,7 +114,7 @@ class FeedURLContainerView: UIView { do { let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[siteNameLabel]-|", options: [], metrics: nil, views: views) - let metrics = [ + let metrics: [String: AnyObject] = [ "top": compressionMode ? 4 : 8, "gap": compressionMode ? 4 : 8, "bottom": compressionMode ? 4 : 8, @@ -126,7 +126,7 @@ class FeedURLContainerView: UIView { } do { - let metrics = [ + let metrics: [String: AnyObject] = [ "imageSize": compressionMode ? 35 : 40, ] diff --git a/Yep/Views/ContainerViews/FeedUploadingErrorContainerView.swift b/Yep/Views/ContainerViews/FeedUploadingErrorContainerView.swift index 70bfae202..e4f334db2 100644 --- a/Yep/Views/ContainerViews/FeedUploadingErrorContainerView.swift +++ b/Yep/Views/ContainerViews/FeedUploadingErrorContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedUploadingErrorContainerView: UIView { +final class FeedUploadingErrorContainerView: UIView { var retryAction: (() -> Void)? var deleteAction: (() -> Void)? @@ -65,7 +65,7 @@ class FeedUploadingErrorContainerView: UIView { leftContainerView.translatesAutoresizingMaskIntoConstraints = false deleteButton.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "leftContainerView": leftContainerView, "deleteButton": deleteButton, ] @@ -86,7 +86,7 @@ class FeedUploadingErrorContainerView: UIView { errorMessageLabel.translatesAutoresizingMaskIntoConstraints = false retryButton.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "iconImageView": iconImageView, "errorMessageLabel": errorMessageLabel, "retryButton": retryButton, diff --git a/Yep/Views/ContainerViews/FeedVoiceContainerView.swift b/Yep/Views/ContainerViews/FeedVoiceContainerView.swift index b54b15be1..1af6f702c 100644 --- a/Yep/Views/ContainerViews/FeedVoiceContainerView.swift +++ b/Yep/Views/ContainerViews/FeedVoiceContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedVoiceContainerView: UIView { +final class FeedVoiceContainerView: UIView { class func fullWidthWithSampleValuesCount(count: Int, timeLengthString: String) -> CGFloat { let rect = timeLengthString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.voiceTimeLengthTextAttributes, context: nil) @@ -80,7 +80,7 @@ class FeedVoiceContainerView: UIView { voiceSampleView.translatesAutoresizingMaskIntoConstraints = false timeLengthLabel.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "bubbleImageView": bubbleImageView, "playButton": playButton, "voiceSampleView": voiceSampleView, diff --git a/Yep/Views/ContainerViews/IconTitleContainerView.swift b/Yep/Views/ContainerViews/IconTitleContainerView.swift index 03919bb26..e2e68abb3 100644 --- a/Yep/Views/ContainerViews/IconTitleContainerView.swift +++ b/Yep/Views/ContainerViews/IconTitleContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class IconTitleContainerView: UIView { +final class IconTitleContainerView: UIView { var tapAction: (() -> Void)? @@ -43,7 +43,7 @@ class IconTitleContainerView: UIView { iconImageView.translatesAutoresizingMaskIntoConstraints = false titleLabel.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "iconImageView": iconImageView, "titleLabel": titleLabel, ] diff --git a/Yep/Views/ContainerViews/LinkContainerView.swift b/Yep/Views/ContainerViews/LinkContainerView.swift index 186c2638a..c947a4100 100644 --- a/Yep/Views/ContainerViews/LinkContainerView.swift +++ b/Yep/Views/ContainerViews/LinkContainerView.swift @@ -8,7 +8,7 @@ import UIKit -class LinkContainerView: UIView { +final class LinkContainerView: UIView { var tapAction: (() -> Void)? @@ -52,7 +52,7 @@ class LinkContainerView: UIView { textLabel.translatesAutoresizingMaskIntoConstraints = false accessoryImageView.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "iconImageView": iconImageView, "textLabel": textLabel, "accessoryImageView": accessoryImageView, diff --git a/Yep/Views/ConversationTitle/ConversationTitleView.swift b/Yep/Views/ConversationTitle/ConversationTitleView.swift index 491a427de..8af3eb1ec 100644 --- a/Yep/Views/ConversationTitle/ConversationTitleView.swift +++ b/Yep/Views/ConversationTitle/ConversationTitleView.swift @@ -8,7 +8,7 @@ import UIKit -class ConversationTitleView: UIView { +final class ConversationTitleView: UIView { lazy var nameLabel: UILabel = { let label = UILabel() @@ -44,7 +44,7 @@ class ConversationTitleView: UIView { helperView.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "nameLabel": nameLabel, "stateInfoLabel": stateInfoLabel, "helperView": helperView, diff --git a/Yep/Views/Feed/FeedMediaView.swift b/Yep/Views/Feed/FeedMediaView.swift index d4bdc753e..1b12b5dd5 100644 --- a/Yep/Views/Feed/FeedMediaView.swift +++ b/Yep/Views/Feed/FeedMediaView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedMediaView: UIView { +final class FeedMediaView: UIView { var attachmentURLs = [NSURL]() @@ -43,8 +43,8 @@ class FeedMediaView: UIView { func setImagesWithAttachments(attachments: [DiscoveredAttachment]) { let fullRect = bounds - let halfRect = CGRect(x: 0, y: 0, width: fullRect.width * 0.5, height: fullRect.height) - let quarterRect = CGRect(x: 0, y: 0, width: fullRect.width * 0.5, height: fullRect.height * 0.5) + let halfRect = CGRect(x: 0, y: 0, width: fullRect.width * 0.5 - 0.5, height: fullRect.height) + let quarterRect = CGRect(x: 0, y: 0, width: fullRect.width * 0.5 - 0.5, height: fullRect.height * 0.5 - 0.5) hidden = (attachments.count == 0) @@ -76,7 +76,7 @@ class FeedMediaView: UIView { } imageView2.frame = halfRect - imageView2.center = CGPoint(x: halfRect.width * 1.5, y: imageView2.center.y) + imageView2.center = CGPoint(x: halfRect.width * 1.5 + 1.0, y: imageView2.center.y) if let thumbnailImage = attachments[1].thumbnailImage { imageView2.image = thumbnailImage @@ -99,7 +99,7 @@ class FeedMediaView: UIView { } imageView2.frame = quarterRect - imageView2.center = CGPoint(x: imageView2.center.x, y: quarterRect.height * 1.5) + imageView2.center = CGPoint(x: imageView2.center.x, y: quarterRect.height * 1.5 + 1) if let thumbnailImage = attachments[1].thumbnailImage { imageView2.image = thumbnailImage @@ -109,7 +109,7 @@ class FeedMediaView: UIView { } imageView3.frame = halfRect - imageView3.center = CGPoint(x: halfRect.width * 1.5, y: imageView3.center.y) + imageView3.center = CGPoint(x: halfRect.width * 1.5 + 1, y: imageView3.center.y) if let thumbnailImage = attachments[2].thumbnailImage { imageView3.image = thumbnailImage @@ -134,7 +134,7 @@ class FeedMediaView: UIView { } imageView2.frame = quarterRect - imageView2.center = CGPoint(x: imageView2.center.x, y: quarterRect.height * 1.5) + imageView2.center = CGPoint(x: imageView2.center.x, y: quarterRect.height * 1.5 + 1) if let thumbnailImage = attachments[1].thumbnailImage { imageView2.image = thumbnailImage @@ -144,7 +144,7 @@ class FeedMediaView: UIView { } imageView3.frame = quarterRect - imageView3.center = CGPoint(x: quarterRect.width * 1.5, y: imageView3.center.y) + imageView3.center = CGPoint(x: quarterRect.width * 1.5 + 1, y: imageView3.center.y) if let thumbnailImage = attachments[2].thumbnailImage { imageView3.image = thumbnailImage @@ -154,7 +154,7 @@ class FeedMediaView: UIView { } imageView4.frame = quarterRect - imageView4.center = CGPoint(x: quarterRect.width * 1.5, y: quarterRect.height * 1.5) + imageView4.center = CGPoint(x: quarterRect.width * 1.5 + 1, y: quarterRect.height * 1.5 + 1) if let thumbnailImage = attachments[3].thumbnailImage { imageView4.image = thumbnailImage diff --git a/Yep/Views/Feed/FeedView.swift b/Yep/Views/Feed/FeedView.swift index eb7418e4c..2f6463f5a 100644 --- a/Yep/Views/Feed/FeedView.swift +++ b/Yep/Views/Feed/FeedView.swift @@ -12,7 +12,7 @@ import AVFoundation import RealmSwift import MapKit -class FeedView: UIView { +final class FeedView: UIView { var feed: ConversationFeed? { willSet { @@ -148,7 +148,7 @@ class FeedView: UIView { view.translatesAutoresizingMaskIntoConstraints = false self.socialWorkContainerView.addSubview(view) - let views = [ + let views: [String: AnyObject] = [ "view": view ] @@ -194,7 +194,7 @@ class FeedView: UIView { view.translatesAutoresizingMaskIntoConstraints = false self.socialWorkContainerView.addSubview(view) - let views = [ + let views: [String: AnyObject] = [ "view": view ] @@ -219,7 +219,7 @@ class FeedView: UIView { view.translatesAutoresizingMaskIntoConstraints = false self.socialWorkContainerView.addSubview(view) - let views = [ + let views: [String: AnyObject] = [ "view": view ] diff --git a/Yep/Views/Footers/SearchFeedsFooterView.swift b/Yep/Views/Footers/SearchFeedsFooterView.swift index 3e82082dd..e455bad39 100644 --- a/Yep/Views/Footers/SearchFeedsFooterView.swift +++ b/Yep/Views/Footers/SearchFeedsFooterView.swift @@ -8,42 +8,170 @@ import UIKit +final private class KeywordCell: UITableViewCell { + + static let reuseIdentifier = "KeywordCell" + + lazy var keywordLabel: UILabel = { + let label = UILabel() + label.textAlignment = .Center + label.textColor = UIColor.yepTintColor() + label.font = UIFont.systemFontOfSize(18) + label.opaque = true + label.backgroundColor = UIColor.whiteColor() + label.clipsToBounds = true + + return label + }() + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + selectionStyle = .None + + makeUI() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func makeUI() { + + contentView.addSubview(keywordLabel) + + keywordLabel.translatesAutoresizingMaskIntoConstraints = false + + let centerX = NSLayoutConstraint(item: keywordLabel, attribute: .CenterX, relatedBy: .Equal, toItem: contentView, attribute: .CenterX, multiplier: 1.0, constant: 0) + let centerY = NSLayoutConstraint(item: keywordLabel, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.0, constant: 0) + + NSLayoutConstraint.activateConstraints([centerX, centerY]) + } +} + class SearchFeedsFooterView: UIView { + enum Style { + case Empty + case Keywords + case Searching + case NoResults + } + + var style: Style = .Empty { + didSet { + switch style { + + case .Empty: + + promptLabel.hidden = true + activityIndicatorView.stopAnimating() + + keywordsTableView.hidden = true + coverView.hidden = true + + case .Keywords: + + promptLabel.hidden = false + promptLabel.textColor = UIColor.darkGrayColor() + promptLabel.text = NSLocalizedString("Try keywords", comment: "") + + activityIndicatorView.stopAnimating() + + keywordsTableView.hidden = false + coverView.hidden = false + + hotWordsOfSearchFeeds(failureHandler: nil) { [weak self] hotwords in + self?.keywords = hotwords + } + + case .Searching: + + promptLabel.hidden = true + + activityIndicatorView.startAnimating() + + keywordsTableView.hidden = true + coverView.hidden = true + + case .NoResults: + + promptLabel.hidden = false + promptLabel.textColor = UIColor.yep_mangmorGrayColor() + promptLabel.text = NSLocalizedString("No search results.", comment: "") + + activityIndicatorView.stopAnimating() + + keywordsTableView.hidden = true + coverView.hidden = true + } + } + } + + var tapCoverAction: (() -> Void)? + var tapKeywordAction: ((keyword: String) -> Void)? + + var keywords: [String] = [] { + didSet { + dispatch_async(dispatch_get_main_queue()) { [weak self] in + self?.keywordsTableView.reloadData() + } + } + } + lazy var promptLabel: UILabel = { let label = UILabel() - label.font = UIFont.systemFontOfSize(17) + label.font = UIFont.systemFontOfSize(20) label.textColor = UIColor.darkGrayColor() label.textAlignment = .Center label.text = NSLocalizedString("Try any keywords", comment: "") return label }() - lazy var keywordLabelA: UILabel = { + lazy var activityIndicatorView: UIActivityIndicatorView = { - let label = UILabel() - label.font = UIFont.systemFontOfSize(13) - label.textColor = UIColor.yepTintColor() - label.textAlignment = .Center - label.text = NSLocalizedString("iOS, Music ...", comment: "") - return label + let view = UIActivityIndicatorView() + view.activityIndicatorViewStyle = .Gray + view.hidesWhenStopped = true + return view }() - lazy var keywordLabelB: UILabel = { + lazy var keywordsTableView: UITableView = { - let label = UILabel() - label.font = UIFont.systemFontOfSize(13) - label.textColor = UIColor.yepTintColor() - label.textAlignment = .Center - label.text = NSLocalizedString("Book, Food ...", comment: "") - return label + let tableView = UITableView() + tableView.registerClass(KeywordCell.self, forCellReuseIdentifier: KeywordCell.reuseIdentifier) + tableView.dataSource = self + tableView.delegate = self + tableView.rowHeight = 36 + tableView.scrollEnabled = false + tableView.separatorStyle = .None + return tableView }() + lazy var coverView: UIView = { + + let view = UIView() + view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.2) + + let tap = UITapGestureRecognizer(target: self, action: #selector(SearchFeedsFooterView.tapCoverView(_:))) + view.addGestureRecognizer(tap) + return view + }() + + @objc private func tapCoverView(sender: UITapGestureRecognizer) { + + coverView.hidden = true + + tapCoverAction?() + } + override init(frame: CGRect) { super.init(frame: frame) makeUI() + + style = .Empty } required init?(coder aDecoder: NSCoder) { @@ -51,26 +179,75 @@ class SearchFeedsFooterView: UIView { } func makeUI() { + addSubview(promptLabel) - addSubview(keywordLabelA) - addSubview(keywordLabelB) + addSubview(activityIndicatorView) + addSubview(keywordsTableView) + addSubview(coverView) promptLabel.translatesAutoresizingMaskIntoConstraints = false - keywordLabelA.translatesAutoresizingMaskIntoConstraints = false - keywordLabelB.translatesAutoresizingMaskIntoConstraints = false + activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false + keywordsTableView.translatesAutoresizingMaskIntoConstraints = false + coverView.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "promptLabel": promptLabel, - "keywordLabelA": keywordLabelA, - "keywordLabelB": keywordLabelB, + "keywordsTableView": keywordsTableView, + "coverView": coverView, ] let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[promptLabel]|", options: [], metrics: nil, views: views) - let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|-40-[promptLabel]-20-[keywordLabelA]-10-[keywordLabelB]-(>=0)-|", options: [.AlignAllCenterX], metrics: nil, views: views) + let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|-40-[promptLabel]-15-[keywordsTableView]|", options: [.AlignAllCenterX, .AlignAllLeading], metrics: nil, views: views) NSLayoutConstraint.activateConstraints(constraintsH) NSLayoutConstraint.activateConstraints(constraintsV) + + do { + let centerX = activityIndicatorView.centerXAnchor.constraintEqualToAnchor(promptLabel.centerXAnchor) + let centerY = activityIndicatorView.centerYAnchor.constraintEqualToAnchor(promptLabel.centerYAnchor) + + NSLayoutConstraint.activateConstraints([centerX, centerY]) + } + + do { + let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[coverView]|", options: [], metrics: nil, views: views) + let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|[coverView]|", options: [], metrics: nil, views: views) + + NSLayoutConstraint.activateConstraints(constraintsH) + NSLayoutConstraint.activateConstraints(constraintsV) + } + } +} + +extension SearchFeedsFooterView: UITableViewDataSource, UITableViewDelegate { + + func numberOfSectionsInTableView(tableView: UITableView) -> Int { + + return 1 + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + + return keywords.count + } + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + + let cell = tableView.dequeueReusableCellWithIdentifier(KeywordCell.reuseIdentifier) as! KeywordCell + let keyword = keywords[indexPath.row] + cell.keywordLabel.text = keyword + return cell + } + + func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + + defer { + tableView.deselectRowAtIndexPath(indexPath, animated: true) + } + + let keyword = keywords[indexPath.row] + tapKeywordAction?(keyword: keyword) } } diff --git a/Yep/Views/FriendRequest/FriendRequestView.swift b/Yep/Views/FriendRequest/FriendRequestView.swift index 5dac51740..552c7a30c 100644 --- a/Yep/Views/FriendRequest/FriendRequestView.swift +++ b/Yep/Views/FriendRequest/FriendRequestView.swift @@ -8,7 +8,7 @@ import UIKit -class FriendRequestView: UIView { +final class FriendRequestView: UIView { static let height: CGFloat = 60 @@ -191,7 +191,7 @@ class FriendRequestView: UIView { stateLabel.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(stateLabel) - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "visualEffectView": visualEffectView, "containerView": containerView, ] diff --git a/Yep/Views/HorizontalLine/HorizontalLineView.swift b/Yep/Views/HorizontalLine/HorizontalLineView.swift index d591c4dbd..a456130e2 100644 --- a/Yep/Views/HorizontalLine/HorizontalLineView.swift +++ b/Yep/Views/HorizontalLine/HorizontalLineView.swift @@ -9,7 +9,7 @@ import UIKit @IBDesignable -class HorizontalLineView: UIView { +final class HorizontalLineView: UIView { @IBInspectable var lineColor: UIColor = UIColor.yepBorderColor() { diff --git a/Yep/Views/InfoView/InfoView.swift b/Yep/Views/InfoView/InfoView.swift index dcf816ac7..5e2ae8630 100644 --- a/Yep/Views/InfoView/InfoView.swift +++ b/Yep/Views/InfoView/InfoView.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class InfoView: UIView { +final class InfoView: UIView { var info: String? @@ -45,7 +45,7 @@ class InfoView: UIView { addSubview(label) - let views = [ + let views: [String: AnyObject] = [ "label": label ] diff --git a/Yep/Views/Labels/FPSLabel.swift b/Yep/Views/Labels/FPSLabel.swift index 0ab1350d1..127abe705 100644 --- a/Yep/Views/Labels/FPSLabel.swift +++ b/Yep/Views/Labels/FPSLabel.swift @@ -8,7 +8,7 @@ import UIKit -class FPSLabel: UILabel { +final class FPSLabel: UILabel { private var displayLink: CADisplayLink? private var lastTime: NSTimeInterval = 0 diff --git a/Yep/Views/Labels/NavigationTitleLabel.swift b/Yep/Views/Labels/NavigationTitleLabel.swift index b7685e79a..6e604612f 100644 --- a/Yep/Views/Labels/NavigationTitleLabel.swift +++ b/Yep/Views/Labels/NavigationTitleLabel.swift @@ -8,7 +8,7 @@ import UIKit -class NavigationTitleLabel: UILabel { +final class NavigationTitleLabel: UILabel { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") diff --git a/Yep/Views/Media/MediaControlView.swift b/Yep/Views/Media/MediaControlView.swift index effa0c4ce..f967f0080 100644 --- a/Yep/Views/Media/MediaControlView.swift +++ b/Yep/Views/Media/MediaControlView.swift @@ -9,7 +9,7 @@ import UIKit //@IBDesignable -class MediaControlView: UIView { +final class MediaControlView: UIView { enum Type { case Image @@ -102,7 +102,7 @@ class MediaControlView: UIView { playButton.translatesAutoresizingMaskIntoConstraints = false shareButton.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "timeLable": timeLabel, "playButton": playButton, "shareButton": shareButton, diff --git a/Yep/Views/Media/MediaPreviewView.swift b/Yep/Views/Media/MediaPreviewView.swift index f2f0b2b84..3073e291c 100644 --- a/Yep/Views/Media/MediaPreviewView.swift +++ b/Yep/Views/Media/MediaPreviewView.swift @@ -9,7 +9,7 @@ import UIKit import AVFoundation -class MediaPreviewView: UIView { +final class MediaPreviewView: UIView { weak var parentViewController: UIViewController? @@ -174,7 +174,7 @@ class MediaPreviewView: UIView { mediaView.translatesAutoresizingMaskIntoConstraints = false mediaControlView.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "mediaView": mediaView, "mediaControlView": mediaControlView, ] diff --git a/Yep/Views/Media/MediaView.swift b/Yep/Views/Media/MediaView.swift index add9a8f04..5efe4c528 100644 --- a/Yep/Views/Media/MediaView.swift +++ b/Yep/Views/Media/MediaView.swift @@ -9,7 +9,7 @@ import UIKit import AVFoundation -class MediaView: UIView { +final class MediaView: UIView { var inTapZoom: Bool = false var isRoomIn: Bool = false @@ -164,7 +164,7 @@ class MediaView: UIView { scrollView.translatesAutoresizingMaskIntoConstraints = false coverImageView.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "scrollView": scrollView, "imageView": imageView, "coverImageView": coverImageView, diff --git a/Yep/Views/Mention/MentionView.swift b/Yep/Views/Mention/MentionView.swift index 7cb880fee..906e187ce 100644 --- a/Yep/Views/Mention/MentionView.swift +++ b/Yep/Views/Mention/MentionView.swift @@ -59,7 +59,7 @@ private class MentionUserCell: UITableViewCell { nicknameLabel.translatesAutoresizingMaskIntoConstraints = false mentionUsernameLabel.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "avatarImageView": avatarImageView, "nicknameLabel": nicknameLabel, "mentionUsernameLabel": mentionUsernameLabel, @@ -90,7 +90,7 @@ private class MentionUserCell: UITableViewCell { } } -class MentionView: UIView { +final class MentionView: UIView { static let height: CGFloat = 125 @@ -148,7 +148,7 @@ class MentionView: UIView { horizontalLineView.translatesAutoresizingMaskIntoConstraints = false tableView.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "horizontalLineView": horizontalLineView, "tableView": tableView, ] diff --git a/Yep/Views/MessageToolbar/MessageToolbar.swift b/Yep/Views/MessageToolbar/MessageToolbar.swift index 2bb1dcb84..6e6307bc8 100644 --- a/Yep/Views/MessageToolbar/MessageToolbar.swift +++ b/Yep/Views/MessageToolbar/MessageToolbar.swift @@ -42,7 +42,7 @@ enum MessageToolbarState: Int, CustomStringConvertible { } @IBDesignable -class MessageToolbar: UIToolbar { +final class MessageToolbar: UIToolbar { var lastToolbarFrame: CGRect? @@ -236,6 +236,8 @@ class MessageToolbar: UIToolbar { return button }() + private var searchTask: CancelableTask? + // MARK: UI override func didMoveToSuperview() { @@ -263,7 +265,7 @@ class MessageToolbar: UIToolbar { self.addSubview(sendButton) sendButton.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "moreButton": moreButton, "messageTextView": messageTextView, "micButton": micButton, @@ -521,6 +523,8 @@ extension MessageToolbar: UITextViewDelegate { if needDetectMention { + cancel(searchTask) + // 刚刚输入 @ if text.hasSuffix("@") { @@ -529,60 +533,63 @@ extension MessageToolbar: UITextViewDelegate { return } - // 对于拼音输入法等,输入时会先显示拼音,然后才上字,拼音间有空格(这个空格似乎不是普通空格) + searchTask = delay(0.4) { [weak self] in - if let markedTextRange = textView.markedTextRange, markedText = textView.textInRange(markedTextRange) { + // 对于拼音输入法等,输入时会先显示拼音,然后才上字,拼音间有空格(这个空格似乎不是普通空格) - var text = text + if let markedTextRange = textView.markedTextRange, markedText = textView.textInRange(markedTextRange) { - let beginning = textView.beginningOfDocument - let start = markedTextRange.start - let end = markedTextRange.end - let location = textView.offsetFromPosition(beginning, toPosition: start) + var text = text - // 保证前面至少还有一个字符,for mentionNSRange - guard location > 0 else { - return - } + let beginning = textView.beginningOfDocument + let start = markedTextRange.start + let end = markedTextRange.end + let location = textView.offsetFromPosition(beginning, toPosition: start) - let length = textView.offsetFromPosition(start, toPosition: end) - let nsRange = NSMakeRange(location, length) - let mentionNSRange = NSMakeRange(location - 1, length + 1) - guard let range = text.yep_rangeFromNSRange(nsRange), mentionRange = text.yep_rangeFromNSRange(mentionNSRange) else { - return - } + // 保证前面至少还有一个字符,for mentionNSRange + guard location > 0 else { + return + } - text.removeRange(range) + let length = textView.offsetFromPosition(start, toPosition: end) + let nsRange = NSMakeRange(location, length) + let mentionNSRange = NSMakeRange(location - 1, length + 1) + guard let range = text.yep_rangeFromNSRange(nsRange), mentionRange = text.yep_rangeFromNSRange(mentionNSRange) else { + return + } - if text.hasSuffix("@") { - mentionUsernameRange = mentionRange + text.removeRange(range) - let wordString = markedText.yep_removeAllWhitespaces - println("wordString from markedText: >\(wordString)<") - tryMentionUserAction?(usernamePrefix: wordString) + if text.hasSuffix("@") { + self?.mentionUsernameRange = mentionRange - return + let wordString = markedText.yep_removeAllWhitespaces + println("wordString from markedText: >\(wordString)<") + self?.tryMentionUserAction?(usernamePrefix: wordString) + + return + } } - } - // 正常查询 mention + // 正常查询 mention - let currentLetterIndex = textView.selectedRange.location - 1 + let currentLetterIndex = textView.selectedRange.location - 1 - if let (wordString, mentionWordRange) = text.yep_mentionWordInIndex(currentLetterIndex) { - //println("mentionWord: \(wordString), \(mentionWordRange)") + if let (wordString, mentionWordRange) = text.yep_mentionWordInIndex(currentLetterIndex) { + //println("mentionWord: \(wordString), \(mentionWordRange)") - mentionUsernameRange = mentionWordRange + self?.mentionUsernameRange = mentionWordRange - let wordString = wordString.trimming(.Whitespace) - tryMentionUserAction?(usernamePrefix: wordString) + let wordString = wordString.trimming(.Whitespace) + self?.tryMentionUserAction?(usernamePrefix: wordString) - return - } + return + } - // 都没有就放弃 + // 都没有就放弃 - giveUpMentionUserAction?() + self?.giveUpMentionUserAction?() + } } } } diff --git a/Yep/Views/MessageToolbar/VoiceRecordButton.swift b/Yep/Views/MessageToolbar/VoiceRecordButton.swift index 94e37f11b..54298b94d 100644 --- a/Yep/Views/MessageToolbar/VoiceRecordButton.swift +++ b/Yep/Views/MessageToolbar/VoiceRecordButton.swift @@ -8,7 +8,7 @@ import UIKit -class VoiceRecordButton: UIView { +final class VoiceRecordButton: UIView { var touchesBegin: (() -> Void)? @@ -126,7 +126,7 @@ class VoiceRecordButton: UIView { self.addSubview(rightVoiceImageView) rightVoiceImageView.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "leftVoiceImageView": leftVoiceImageView, "titleLabel": titleLabel, "rightVoiceImageView": rightVoiceImageView, diff --git a/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift b/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift index 9191cfa9f..e5a378dfb 100644 --- a/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift +++ b/Yep/Views/MoreMessageTypes/MoreMessageTypesView.swift @@ -9,7 +9,7 @@ import UIKit import Photos -class MoreMessageTypesView: UIView { +final class MoreMessageTypesView: UIView { let totalHeight: CGFloat = 100 + 60 * 3 @@ -135,7 +135,7 @@ class MoreMessageTypesView: UIView { containerView.addSubview(tableView) tableView.translatesAutoresizingMaskIntoConstraints = false - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "containerView": containerView, "tableView": tableView, ] diff --git a/Yep/Views/PickerItems/FeedSkill/FeedSkillPickerItemView.swift b/Yep/Views/PickerItems/FeedSkill/FeedSkillPickerItemView.swift index 76c5d9492..01a76142f 100644 --- a/Yep/Views/PickerItems/FeedSkill/FeedSkillPickerItemView.swift +++ b/Yep/Views/PickerItems/FeedSkill/FeedSkillPickerItemView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedSkillPickerItemView: UIView { +final class FeedSkillPickerItemView: UIView { lazy var bubbleImageView: UIImageView = { let view = UIImageView(image: UIImage(named: "skill_bubble")!) diff --git a/Yep/Views/ProgressViews/MessageLoadingProgressView.swift b/Yep/Views/ProgressViews/MessageLoadingProgressView.swift index d87caf6c3..616316937 100644 --- a/Yep/Views/ProgressViews/MessageLoadingProgressView.swift +++ b/Yep/Views/ProgressViews/MessageLoadingProgressView.swift @@ -8,7 +8,7 @@ import UIKit -class MessageLoadingProgressView: UIView { +final class MessageLoadingProgressView: UIView { var progress: Double = 0.0 { didSet { diff --git a/Yep/Views/PullToRefresh/PullToRefreshView.swift b/Yep/Views/PullToRefresh/PullToRefreshView.swift index 5263e6c54..1cfa1882e 100644 --- a/Yep/Views/PullToRefresh/PullToRefreshView.swift +++ b/Yep/Views/PullToRefresh/PullToRefreshView.swift @@ -15,7 +15,7 @@ protocol PullToRefreshViewDelegate: class { private let sceneHeight: CGFloat = 80 -class PullToRefreshView: UIView { +final class PullToRefreshView: UIView { var refreshView: YepRefreshView! @@ -162,7 +162,7 @@ extension PullToRefreshView: UIScrollViewDelegate { } } -class RefreshItem { +final class RefreshItem { unowned var view: UIView diff --git a/Yep/Views/PullToRefresh/YepRefreshView.swift b/Yep/Views/PullToRefresh/YepRefreshView.swift index 12250fef5..81075eb55 100644 --- a/Yep/Views/PullToRefresh/YepRefreshView.swift +++ b/Yep/Views/PullToRefresh/YepRefreshView.swift @@ -10,7 +10,7 @@ import UIKit import QuartzCore -class YepShape: CAShapeLayer { +final class YepShape: CAShapeLayer { let shapeColor = UIColor(red:0.33, green:0.71, blue:0.98, alpha:1) @@ -50,7 +50,7 @@ class YepShape: CAShapeLayer { } -class YepRefreshView: UIView { +final class YepRefreshView: UIView { var shapes = [YepShape]() diff --git a/Yep/Views/Rank/RankView.swift b/Yep/Views/Rank/RankView.swift index 87ec494a1..079513a69 100644 --- a/Yep/Views/Rank/RankView.swift +++ b/Yep/Views/Rank/RankView.swift @@ -9,7 +9,7 @@ import UIKit @IBDesignable -class RankView: UIView { +final class RankView: UIView { @IBInspectable var barNumber: Int = 4 @IBInspectable var barColor: UIColor = UIColor.yepTintColor() diff --git a/Yep/Views/ReusableViews/AddSkills/AddSkillsReusableView.swift b/Yep/Views/ReusableViews/AddSkills/AddSkillsReusableView.swift index 326070e5f..c05adb050 100644 --- a/Yep/Views/ReusableViews/AddSkills/AddSkillsReusableView.swift +++ b/Yep/Views/ReusableViews/AddSkills/AddSkillsReusableView.swift @@ -9,7 +9,7 @@ import UIKit import Ruler -class AddSkillsReusableView: UICollectionReusableView { +final class AddSkillsReusableView: UICollectionReusableView { var skillSet: SkillSet = .Master { willSet { diff --git a/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.swift b/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.swift index 90498f8c9..ad4a0358b 100644 --- a/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.swift +++ b/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.swift @@ -8,7 +8,7 @@ import UIKit -class ProfileSectionHeaderReusableView: UICollectionReusableView { +final class ProfileSectionHeaderReusableView: UICollectionReusableView { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var titleLabelLeadingConstraint: NSLayoutConstraint! diff --git a/Yep/Views/ReusableViews/SkillAnnotationHeader/SkillAnnotationHeader.swift b/Yep/Views/ReusableViews/SkillAnnotationHeader/SkillAnnotationHeader.swift index 7d3dbc6eb..5bd756ae7 100644 --- a/Yep/Views/ReusableViews/SkillAnnotationHeader/SkillAnnotationHeader.swift +++ b/Yep/Views/ReusableViews/SkillAnnotationHeader/SkillAnnotationHeader.swift @@ -8,7 +8,7 @@ import UIKit -class SkillAnnotationHeader: UICollectionReusableView { +final class SkillAnnotationHeader: UICollectionReusableView { @IBOutlet weak var annotationLabel: UILabel! } diff --git a/Yep/Views/ReusableViews/TableSectionTitle/TableSectionTitleView.swift b/Yep/Views/ReusableViews/TableSectionTitle/TableSectionTitleView.swift index 1f7d8b592..2859c61f7 100644 --- a/Yep/Views/ReusableViews/TableSectionTitle/TableSectionTitleView.swift +++ b/Yep/Views/ReusableViews/TableSectionTitle/TableSectionTitleView.swift @@ -8,7 +8,7 @@ import UIKit -class TableSectionTitleView: UITableViewHeaderFooterView { +final class TableSectionTitleView: UITableViewHeaderFooterView { lazy var titleLabel: UILabel = { let label = UILabel() @@ -23,7 +23,7 @@ class TableSectionTitleView: UITableViewHeaderFooterView { contentView.addSubview(titleLabel) titleLabel.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "titleLabel": titleLabel, ] diff --git a/Yep/Views/SayHi/BottomButtonView.swift b/Yep/Views/SayHi/BottomButtonView.swift index 9d6810fee..800bfff26 100644 --- a/Yep/Views/SayHi/BottomButtonView.swift +++ b/Yep/Views/SayHi/BottomButtonView.swift @@ -9,7 +9,7 @@ import UIKit @IBDesignable -class BottomButtonView: UIView { +final class BottomButtonView: UIView { @IBInspectable var topLineColor: UIColor = UIColor.yepBorderColor() @IBInspectable var topLineWidth: CGFloat = 1 / UIScreen.mainScreen().scale @@ -50,7 +50,7 @@ class BottomButtonView: UIView { let actionButtonHeightConstraint = NSLayoutConstraint(item: actionButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 30) - let constraints = [ + let constraints: [NSLayoutConstraint] = [ actionButtonCenterXConstraint, actionButtonCenterYConstraint, actionButtonWidthConstraint, diff --git a/Yep/Views/ScrollView/YepChildScrollView.swift b/Yep/Views/ScrollView/YepChildScrollView.swift index 34dcca9ef..b4d248cdd 100644 --- a/Yep/Views/ScrollView/YepChildScrollView.swift +++ b/Yep/Views/ScrollView/YepChildScrollView.swift @@ -8,7 +8,7 @@ import UIKit -class YepChildScrollView: UITableView { +final class YepChildScrollView: UITableView { func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { diff --git a/Yep/Views/ShareProfileView/ShareProfileView.swift b/Yep/Views/ShareProfileView/ShareProfileView.swift index 6fbae0c26..400da4a40 100644 --- a/Yep/Views/ShareProfileView/ShareProfileView.swift +++ b/Yep/Views/ShareProfileView/ShareProfileView.swift @@ -8,7 +8,7 @@ import UIKit -class ShareProfileView: UIView { +final class ShareProfileView: UIView { var progress: CGFloat = 0 diff --git a/Yep/Views/SkillHomeHeaderView/SkillHomeHeaderView.swift b/Yep/Views/SkillHomeHeaderView/SkillHomeHeaderView.swift index 013c9d290..0dec291df 100644 --- a/Yep/Views/SkillHomeHeaderView/SkillHomeHeaderView.swift +++ b/Yep/Views/SkillHomeHeaderView/SkillHomeHeaderView.swift @@ -9,7 +9,7 @@ import UIKit import Kingfisher -class SkillHomeHeaderView: UIView { +final class SkillHomeHeaderView: UIView { var skillCategory: SkillCell.Skill.Category = .Art var skillCoverURLString: String? { diff --git a/Yep/Views/SkillHomeSectionButton/SkillHomeSectionButton.swift b/Yep/Views/SkillHomeSectionButton/SkillHomeSectionButton.swift index 4805a2103..d0d189c75 100644 --- a/Yep/Views/SkillHomeSectionButton/SkillHomeSectionButton.swift +++ b/Yep/Views/SkillHomeSectionButton/SkillHomeSectionButton.swift @@ -11,7 +11,7 @@ import pop let skillHeomSectionButtonLineHeight: CGFloat = 2 -class SkillHomeSectionButton: UIButton { +final class SkillHomeSectionButton: UIButton { let highLight = CALayer() diff --git a/Yep/Views/Subscribe/SubscribeView.swift b/Yep/Views/Subscribe/SubscribeView.swift index 54956c1a9..050e4aa66 100644 --- a/Yep/Views/Subscribe/SubscribeView.swift +++ b/Yep/Views/Subscribe/SubscribeView.swift @@ -8,7 +8,7 @@ import UIKit -class SubscribeView: UIView { +final class SubscribeView: UIView { static let height: CGFloat = 50 @@ -68,7 +68,7 @@ class SubscribeView: UIView { horizontalLineView.backgroundColor = UIColor.clearColor() horizontalLineView.atBottom = false - let views = [ + let views: [String: AnyObject] = [ "horizontalLineView": horizontalLineView, ] @@ -91,7 +91,7 @@ class SubscribeView: UIView { subscribeButton.translatesAutoresizingMaskIntoConstraints = false dismissButton.translatesAutoresizingMaskIntoConstraints = false - let views = [ + let views: [String: AnyObject] = [ "iconImageView": iconImageView, "promptLabel": promptLabel, "subscribeButton": subscribeButton, diff --git a/Yep/Views/TextFields/BorderTextField.swift b/Yep/Views/TextFields/BorderTextField.swift index ad5690976..d854fcdaf 100644 --- a/Yep/Views/TextFields/BorderTextField.swift +++ b/Yep/Views/TextFields/BorderTextField.swift @@ -9,7 +9,7 @@ import UIKit @IBDesignable -class BorderTextField: UITextField { +final class BorderTextField: UITextField { @IBInspectable var lineColor: UIColor = UIColor.yepBorderColor() @IBInspectable var lineWidth: CGFloat = 1 / UIScreen.mainScreen().scale diff --git a/Yep/Views/TextFields/UnderLineTextField.swift b/Yep/Views/TextFields/UnderLineTextField.swift index 0383e808e..101d1281f 100644 --- a/Yep/Views/TextFields/UnderLineTextField.swift +++ b/Yep/Views/TextFields/UnderLineTextField.swift @@ -9,7 +9,7 @@ import UIKit @IBDesignable -class UnderLineTextField: UITextField { +final class UnderLineTextField: UITextField { @IBInspectable var underLineColor: UIColor = UIColor.yepTintColor() @IBInspectable var underLineWidth: CGFloat = 1 diff --git a/Yep/Views/TextViews/ChatTextView.swift b/Yep/Views/TextViews/ChatTextView.swift index 0f1ba4ae6..abbd5bd79 100644 --- a/Yep/Views/TextViews/ChatTextView.swift +++ b/Yep/Views/TextViews/ChatTextView.swift @@ -8,7 +8,7 @@ import UIKit -class ChatTextView: UITextView { +final class ChatTextView: UITextView { var tapMentionAction: ((username: String) -> Void)? diff --git a/Yep/Views/TextViews/FeedTextView.swift b/Yep/Views/TextViews/FeedTextView.swift index 1015cc9f7..fa4bbd7fc 100644 --- a/Yep/Views/TextViews/FeedTextView.swift +++ b/Yep/Views/TextViews/FeedTextView.swift @@ -8,7 +8,7 @@ import UIKit -class FeedTextView: UITextView { +final class FeedTextView: UITextView { override func canBecomeFirstResponder() -> Bool { return false diff --git a/Yep/Views/TitleViews/ActivityIndicatorTitleView.swift b/Yep/Views/TitleViews/ActivityIndicatorTitleView.swift index d94118552..83fa4c183 100644 --- a/Yep/Views/TitleViews/ActivityIndicatorTitleView.swift +++ b/Yep/Views/TitleViews/ActivityIndicatorTitleView.swift @@ -8,7 +8,7 @@ import UIKit -class ActivityIndicatorTitleView: UIView { +final class ActivityIndicatorTitleView: UIView { enum State { case Normal @@ -62,7 +62,7 @@ class ActivityIndicatorTitleView: UIView { self.singleTitleLabel = label - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "label": label, ] @@ -104,7 +104,7 @@ class ActivityIndicatorTitleView: UIView { NSLayoutConstraint.activateConstraints([helperViewCenterX, helperViewCenterY]) - let viewsDictionary = [ + let viewsDictionary: [String: AnyObject] = [ "activityIndicator": activityIndicator, "label": label, ] diff --git a/Yep/Views/TouchClosures/TouchClosuresView.swift b/Yep/Views/TouchClosures/TouchClosuresView.swift index 95bdd5eab..517d5e193 100644 --- a/Yep/Views/TouchClosures/TouchClosuresView.swift +++ b/Yep/Views/TouchClosures/TouchClosuresView.swift @@ -8,7 +8,7 @@ import UIKit -class TouchClosuresView: UIView { +final class TouchClosuresView: UIView { var touchesBeganAction: (() -> Void)? var touchesEndedAction: (() -> Void)? diff --git a/Yep/Yep-Bridging-Header.h b/Yep/Yep-Bridging-Header.h index d60a18fa5..ace7fde07 100644 --- a/Yep/Yep-Bridging-Header.h +++ b/Yep/Yep-Bridging-Header.h @@ -3,7 +3,11 @@ // #import -#import "APService.h" + +#ifndef DEBUG +#import //#import "JPUSHService.h" +#endif + #import "MZFayeClient.h" #import diff --git a/Yep/en.lproj/Localizable.strings b/Yep/en.lproj/Localizable.strings index dc2571051..3608deab9 100644 --- a/Yep/en.lproj/Localizable.strings +++ b/Yep/en.lproj/Localizable.strings @@ -94,6 +94,9 @@ /* No comment provided by engineer. */ "Choose Photo" = "Choose Photo"; +/* No comment provided by engineer. */ +"Choose from Library" = "Choose from Library"; + /* No comment provided by engineer. */ "Choose photo" = "Choose photo"; @@ -347,7 +350,10 @@ /* No comment provided by engineer. */ "recording" = "recording"; -/* No comment provided by engineer. */ +//* No comment provided by engineer. */ +"Recently Deleted"= "Recently Deleted"; + +* No comment provided by engineer. */ "Reject" = "Reject"; /* No comment provided by engineer. */ @@ -537,7 +543,7 @@ "You have blocked %@! Do you want to unblock him or her?" = "You have blocked %@! Do you want to unblock him or her?"; /* No comment provided by engineer. */ -"Pods help Yep" = "Pods help Yep"; +"Pods helps Yep" = "Pods helps Yep"; /* No comment provided by engineer. */ "Rate Yep on App Store" = "Rate Yep on App Store"; diff --git a/Yep/zh-Hans.lproj/Localizable.strings b/Yep/zh-Hans.lproj/Localizable.strings index 15bc90ebd..b6dd602fa 100644 --- a/Yep/zh-Hans.lproj/Localizable.strings +++ b/Yep/zh-Hans.lproj/Localizable.strings @@ -99,6 +99,9 @@ /* No comment provided by engineer. */ "Choose photo" = "选择照片"; +/* No comment provided by engineer. */ +"Choose from Library" = "从相册选取"; + /* No comment provided by engineer. */ "Clear history" = "清除历史记录"; @@ -345,6 +348,9 @@ /* No comment provided by engineer. */ "recording" = "录制语音"; +//* No comment provided by engineer. */ +"Recently Deleted" = "最近删除"; + /* No comment provided by engineer. */ "Reject" = "拒绝"; @@ -535,7 +541,7 @@ "You have blocked %@! Do you want to unblock him or her?" = "您已屏蔽“%@”!您要解除屏蔽吗?"; /* No comment provided by engineer. */ -"Pods help Yep" = "Yep 使用的第三方 Pods"; +"Pods helps Yep" = "Yep 使用的第三方 Pods"; /* No comment provided by engineer. */ "Rate Yep on App Store" = "在 App Store 上对 Yep 评分"; @@ -758,9 +764,18 @@ "No blocked creators." = "没有被屏蔽的创建者"; "Ooops! You've been blocked." = "Ooops! 您被对方屏蔽了。"; -"Try any keywords" = "试试任意关键词"; -"iOS, Music ..." = "iOS,音乐…"; -"Book, Food ..." = "书籍,食物…"; +"Try keywords" = "试试这些"; "No more results." = "没有更多搜索结果了"; +"Search feeds in channel" = "搜索频道话题"; +"Search feeds by user" = "搜索用户话题"; + +"Change Mobile" = "修改手机号"; +"What's your new number?" = "您的新手机号码?"; +"Current number:" = "当前号码"; + +"Submit" = "提交"; +"You have successfully updated your mobile for Yep! For now on, using the new number to login." = "您已成功更新您的Yep手机号!以后您可使用新号码进行登录。"; + + diff --git a/YepTests/ClearTests.swift b/YepTests/ClearTests.swift new file mode 100644 index 000000000..3cc0b535d --- /dev/null +++ b/YepTests/ClearTests.swift @@ -0,0 +1,54 @@ +// +// ClearTests.swift +// Yep +// +// Created by NIX on 16/4/27. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest +@testable import Yep +import RealmSwift + +final class ClearTests: XCTestCase { + + func testCleanRealmAndCaches() { + + cleanRealmAndCaches() + + let realm = try! Realm() + + do { + let noMessages = realm.objects(Message).isEmpty + XCTAssertTrue(noMessages) + } + + do { + let noUsers = realm.objects(User).isEmpty + XCTAssertTrue(noUsers) + } + + do { + let noGroups = realm.objects(Group).isEmpty + XCTAssertTrue(noGroups) + } + + do { + let noFeeds = realm.objects(Feed).isEmpty + XCTAssertTrue(noFeeds) + } + + do { + let path = NSFileManager.yepMessageCachesURL()?.path + let noMessageCacheFiles = try! NSFileManager.defaultManager().contentsOfDirectoryAtPath(path!).isEmpty + XCTAssertTrue(noMessageCacheFiles) + } + + do { + let path = NSFileManager.yepAvatarCachesURL()?.path + let noAvatarCacheFiles = try! NSFileManager.defaultManager().contentsOfDirectoryAtPath(path!).isEmpty + XCTAssertTrue(noAvatarCacheFiles) + } + } +} + diff --git a/YepTests/Extensions/NSURL+YepTests.swift b/YepTests/Extensions/NSURL+YepTests.swift new file mode 100644 index 000000000..b445ab19e --- /dev/null +++ b/YepTests/Extensions/NSURL+YepTests.swift @@ -0,0 +1,27 @@ +// +// NSURL+YepTests.swift +// Yep +// +// Created by NIX on 16/4/28. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import Foundation + +extension NSURL { + + private var yeptests_queryItems: [NSURLQueryItem] { + + if let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false), queryItems = components.queryItems { + return queryItems + } + + return [] + } + + func yeptests_containsQueryItem(queryItem: NSURLQueryItem) -> Bool { + + return yeptests_queryItems.contains(queryItem) + } +} + diff --git a/YepTests/Images.xcassets/Contents.json b/YepTests/Images.xcassets/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/YepTests/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/YepTests/Images.xcassets/coolie.imageset/Contents.json b/YepTests/Images.xcassets/coolie.imageset/Contents.json new file mode 100644 index 000000000..2d7d770a4 --- /dev/null +++ b/YepTests/Images.xcassets/coolie.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "coolie.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/YepTests/Images.xcassets/coolie.imageset/coolie.png b/YepTests/Images.xcassets/coolie.imageset/coolie.png new file mode 100644 index 000000000..7795c284c Binary files /dev/null and b/YepTests/Images.xcassets/coolie.imageset/coolie.png differ diff --git a/YepTests/OpenGraphTests.swift b/YepTests/OpenGraphTests.swift new file mode 100644 index 000000000..029d4008e --- /dev/null +++ b/YepTests/OpenGraphTests.swift @@ -0,0 +1,54 @@ +// +// OpenGraphTests.swift +// Yep +// +// Created by NIX on 16/4/28. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest +@testable import Yep + +final class OpenGraphTests: XCTestCase { + + func testBaiduOpenGraph() { + + let baiduURL = NSURL(string: "http://www.baidu.com")! + + let expectation = expectationWithDescription("baidu open graph") + + openGraphWithURL(baiduURL, failureHandler: nil) { openGraph in + + print("baidu openGraph: \(openGraph)") + expectation.fulfill() + } + + waitForExpectationsWithTimeout(5, handler: nil) + + XCTAssert(true, "Pass") + } + + func testItunesOpenGraph() { + + // 单曲 + let iTunesURL = NSURL(string: "https://itunes.apple.com/cn/album/hello-single/id1051365605?i=1051366040&l=en")! + + let queryItem = NSURLQueryItem(name: "at", value: "1010l9k7") + + let expectation = expectationWithDescription("iTunes open graph") + + openGraphWithURL(iTunesURL, failureHandler: nil) { openGraph in + + print("iTunes openGraph: \(openGraph)") + + if openGraph.URL.yeptests_containsQueryItem(queryItem) { + expectation.fulfill() + } + } + + waitForExpectationsWithTimeout(10, handler: nil) + + XCTAssert(true, "Pass") + } +} + diff --git a/YepTests/RealmTests.swift b/YepTests/RealmTests.swift new file mode 100644 index 000000000..c699c2458 --- /dev/null +++ b/YepTests/RealmTests.swift @@ -0,0 +1,50 @@ +// +// RealmTests.swift +// Yep +// +// Created by NIX on 16/4/27. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest +@testable import Yep +import RealmSwift + +final class RealmTests: XCTestCase { + + func testCreateMessageAndDelete() { + + let realm = try! Realm() + + let messages: [Message] = (0..<100).map({ index in + let message = Message() + message.messageID = "test\(index)" + return message + }) + + do { + messages.forEach({ + realm.beginWrite() + realm.add($0) + try! realm.commitWrite() + }) + + let firstMessage = messages.first! + XCTAssertFalse(firstMessage.invalidated) + } + + realm.refresh() + + do { + messages.forEach({ + realm.beginWrite() + realm.delete($0) + try! realm.commitWrite() + }) + + let lastMessage = messages.last! + XCTAssertTrue(lastMessage.invalidated) + } + } +} + diff --git a/YepTests/ServiceTests.swift b/YepTests/ServiceTests.swift new file mode 100644 index 000000000..fd2be6f9d --- /dev/null +++ b/YepTests/ServiceTests.swift @@ -0,0 +1,164 @@ +// +// ServerTests.swift +// Yep +// +// Created by NIX on 16/4/27. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest +@testable import Yep + +final class ServiceTests: XCTestCase { + + func testGetHotWords() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("get hot words") + + hotWordsOfSearchFeeds(failureHandler: nil, completion: { hotWords in + if !hotWords.isEmpty { + expectation.fulfill() + } + }) + + waitForExpectationsWithTimeout(5, handler: nil) + } + + func testGetFeedsWithKeyword() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("get feeds with keyword") + + feedsWithKeyword("hello", skillID: nil, userID: nil, pageIndex: 1, perPage: 30, failureHandler: nil) { feeds in + if !feeds.isEmpty { + expectation.fulfill() + } + } + + waitForExpectationsWithTimeout(5, handler: nil) + } + + func testJoinAndLeaveGroup() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("join and leave group") + + feedsWithKeyword("iOS", skillID: nil, userID: nil, pageIndex: 1, perPage: 1, failureHandler: nil) { feeds in + if let firstFeed = feeds.first { + let groupID = firstFeed.groupID + joinGroup(groupID: groupID, failureHandler: nil, completion: { + leaveGroup(groupID: groupID, failureHandler: nil, completion: { + expectation.fulfill() + }) + }) + } + } + + waitForExpectationsWithTimeout(15, handler: nil) + } + + func testSendMessageToGroup() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("send message to group") + + feedsWithKeyword("Yep", skillID: nil, userID: nil, pageIndex: 1, perPage: 1, failureHandler: nil) { feeds in + + if let firstFeed = feeds.first { + let groupID = firstFeed.groupID + + dispatch_async(dispatch_get_main_queue()) { + sendText("How do you do?", toRecipient: groupID, recipientType: "Circle", afterCreatedMessage: { _ in }, failureHandler: nil, completion: { success in + + if success { + meIsMemberOfGroup(groupID: groupID, failureHandler: nil, completion: { yes in + if yes { + leaveGroup(groupID: groupID, failureHandler: nil, completion: { + }) + + expectation.fulfill() + } + }) + } + }) + } + } + } + + waitForExpectationsWithTimeout(10, handler: nil) + } + + func testUpdateAvatar() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("update avatar") + + let bundle = NSBundle(forClass: ServiceTests.self) + let image = UIImage(named: "coolie", inBundle: bundle, compatibleWithTraitCollection: nil)! + let imageData = UIImageJPEGRepresentation(image, YepConfig.avatarCompressionQuality())! + + updateAvatarWithImageData(imageData, failureHandler: nil, completion: { newAvatarURLString in + userInfo(failureHandler: nil) { myUserInfo in + if let avatarInfo = myUserInfo["avatar"] as? JSONDictionary, avatarURLString = avatarInfo["url"] as? String { + if newAvatarURLString == avatarURLString { + expectation.fulfill() + } + } + } + }) + + waitForExpectationsWithTimeout(10, handler: nil) + } + + func testGetCreatorsOfBlockedFeeds() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("get creators of blocked feeds") + + creatorsOfBlockedFeeds(failureHandler: nil, completion: { creators in + print("creatorsOfBlockedFeeds.count: \(creators.count)") + expectation.fulfill() + }) + + waitForExpectationsWithTimeout(5, handler: nil) + } + + func testGetUsersMatchWithUsernamePrefix() { + + guard YepUserDefaults.isLogined else { + return + } + + let usernamePrefix = "t" + + let expectation = expectationWithDescription("get users match with username prefix: \(usernamePrefix)") + + usersMatchWithUsernamePrefix(usernamePrefix, failureHandler: nil) { users in + if !users.isEmpty { + expectation.fulfill() + } + } + + waitForExpectationsWithTimeout(5, handler: nil) + } +} + diff --git a/YepTests/SyncTests.swift b/YepTests/SyncTests.swift new file mode 100644 index 000000000..94e9a906c --- /dev/null +++ b/YepTests/SyncTests.swift @@ -0,0 +1,65 @@ +// +// SyncTests.swift +// Yep +// +// Created by NIX on 16/4/27. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest +@testable import Yep + +final class SyncTests: XCTestCase { + + func testSyncFriendships() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("sync friendships") + + syncFriendshipsAndDoFurtherAction { + expectation.fulfill() + } + + waitForExpectationsWithTimeout(5, handler: nil) + + XCTAssert(true, "Pass") + } + + func testSyncGroups() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("sync groups") + + syncGroupsAndDoFurtherAction { + expectation.fulfill() + } + + waitForExpectationsWithTimeout(5, handler: nil) + + XCTAssert(true, "Pass") + } + + func testSyncUnreadMessages() { + + guard YepUserDefaults.isLogined else { + return + } + + let expectation = expectationWithDescription("sync unread messages") + + syncUnreadMessagesAndDoFurtherAction { _ in + expectation.fulfill() + } + + waitForExpectationsWithTimeout(5, handler: nil) + + XCTAssert(true, "Pass") + } +} + diff --git a/YepTests/YepTests.swift b/YepTests/YepTests.swift index c46b7aa6e..cbb09a38f 100644 --- a/YepTests/YepTests.swift +++ b/YepTests/YepTests.swift @@ -2,14 +2,13 @@ // YepTests.swift // YepTests // -// Created by kevinzhow on 15/3/16. -// Copyright (c) 2015年 Catch Inc. All rights reserved. +// Created by NIX on 16/4/27. +// Copyright © 2016年 Catch Inc. All rights reserved. // -import UIKit import XCTest -class YepTests: XCTestCase { +final class YepTests: XCTestCase { override func setUp() { super.setUp() @@ -23,12 +22,12 @@ class YepTests: XCTestCase { func testExample() { // This is an example of a functional test case. - XCTAssert(true, "Pass") + // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock() { + self.measureBlock { // Put the code you want to measure the time of here. } } diff --git a/YepUITests/Extensions/XCUIElement+Yep.swift b/YepUITests/Extensions/XCUIElement+Yep.swift new file mode 100644 index 000000000..967bd0ffb --- /dev/null +++ b/YepUITests/Extensions/XCUIElement+Yep.swift @@ -0,0 +1,28 @@ +// +// XCUIElement+Yep.swift +// Yep +// +// Created by NIX on 16/4/26. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest + +extension XCUIElement { + + func clearAndEnterText(text: String) -> Void { + + guard let stringValue = value as? String else { + return + } + + var deleteString: String = "" + for _ in stringValue.characters { + deleteString += "\u{8}" + } + typeText(deleteString) + + typeText(text) + } +} + diff --git a/YepUITests/Info.plist b/YepUITests/Info.plist new file mode 100644 index 000000000..ba72822e8 --- /dev/null +++ b/YepUITests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/YepUITests/YepUITests.swift b/YepUITests/YepUITests.swift new file mode 100644 index 000000000..9ebaefc53 --- /dev/null +++ b/YepUITests/YepUITests.swift @@ -0,0 +1,138 @@ +// +// YepUITests.swift +// YepUITests +// +// Created by NIX on 16/4/25. +// Copyright © 2016年 Catch Inc. All rights reserved. +// + +import XCTest + +final class YepUITests: XCTestCase { + + override func setUp() { + super.setUp() + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPostTextFeed() { + + let app = XCUIApplication() + app.tabBars.buttons["Feeds"].tap() + app.navigationBars["Feeds"].buttons["Add"].tap() + app.tables.staticTexts["Text & Photos"].tap() + + let scrollViewsQuery = app.scrollViews + let textView = scrollViewsQuery.childrenMatchingType(.TextView).element + textView.tap() + textView.typeText("42_1984") + app.navigationBars["New Feed"].buttons["Post"].tap() + } + + func testChangeNickname() { + + let app = XCUIApplication() + app.tabBars.buttons["Profile"].tap() + app.buttons["icon settings"].tap() + + let tablesQuery = app.tables + tablesQuery.cells.elementBoundByIndex(0).tap() + tablesQuery.staticTexts["Nickname"].tap() + + let cell = tablesQuery.childrenMatchingType(.Cell).elementBoundByIndex(0) + let textField = cell.childrenMatchingType(.TextField).element + textField.tap() + textField.clearAndEnterText("NIX\(abs(NSUUID().UUIDString.hash))") + + app.buttons["Done"].tap() + + app.navigationBars["Nickname"].buttons["Edit Profile"].tap() + app.navigationBars["Edit Profile"].buttons["Settings"].tap() + app.navigationBars["Settings"].buttons["Profile"].tap() + } + + func testSearchUsers() { + + let app = XCUIApplication() + app.tabBars.buttons["Contacts"].tap() + app.navigationBars["Contacts"].buttons["Add"].tap() + + let textField = app.tables.childrenMatchingType(.Cell).elementBoundByIndex(0).childrenMatchingType(.TextField).element + textField.tap() + textField.typeText("kevin14") + app.buttons["Search"].tap() + + app.tables.staticTexts["kevin14"].tap() + + app.navigationBars["kevin14"].buttons["icon back"].tap() + } + + func testSearchInConversations() { + + let app = XCUIApplication() + let tablesQuery = app.tables + tablesQuery.searchFields["Search"].tap() + + let textField = app.searchFields["Search"] + textField.tap() + textField.typeText("app") + app.buttons["Done"].tap() + + //tablesQuery.staticTexts["大家期待Pay吗?要看看相关的API了 www.apple.com/cn/apple-pay/"].tap() + + //app.navigationBars["Conversation"].buttons["Search"].tap() + + app.buttons["Cancel"].tap() + } + + func testSearchInContacts() { + + let app = XCUIApplication() + app.tabBars.buttons["Contacts"].tap() + app.tables.searchFields["Search Friend"].tap() + + let textField = app.searchFields["Search Friend"] + textField.tap() + textField.typeText("test") + app.buttons["Done"].tap() + + app.tables.staticTexts["test"].tap() + + app.navigationBars["test"].buttons["icon back"].tap() + + app.buttons["Cancel"].tap() + } + + func testSearchInFeeds() { + + let app = XCUIApplication() + app.tabBars.buttons["Feeds"].tap() + app.tables.searchFields["Search Feeds"].tap() + + let textField = app.searchFields["Search Feeds"] + textField.tap() + textField.typeText("Hello") + app.buttons["Done"].tap() + + app.buttons["Cancel"].tap() + } +} +