From d774f0637091bd89e6f32bfec673c424a7527d8c Mon Sep 17 00:00:00 2001 From: quan Date: Sat, 25 Feb 2017 18:08:57 +0700 Subject: [PATCH] fisrt comit --- .idea/App.iml | 2 + .idea/encodings.xml | 6 + .idea/inspectionProfiles/Project_Default.xml | 5 + .idea/misc.xml | 96 ++ .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/xcode.xml | 4 + App/AppDelegate.swift | 40 + .../AppIcon.appiconset/Contents.json | 106 ++ .../AppIcon.appiconset/ic_launcher-120.png | Bin 0 -> 20748 bytes .../AppIcon.appiconset/ic_launcher-152.png | Bin 0 -> 30725 bytes .../AppIcon.appiconset/ic_launcher-167.png | Bin 0 -> 35774 bytes .../AppIcon.appiconset/ic_launcher-180.png | Bin 0 -> 40569 bytes .../AppIcon.appiconset/ic_launcher-40.png | Bin 0 -> 3337 bytes .../AppIcon.appiconset/ic_launcher-58.png | Bin 0 -> 6207 bytes .../AppIcon.appiconset/ic_launcher-76.png | Bin 0 -> 9838 bytes .../AppIcon.appiconset/ic_launcher-80.png | Bin 0 -> 10920 bytes .../AppIcon.appiconset/ic_launcher-87.png | Bin 0 -> 12139 bytes App/Assets.xcassets/Contents.json | 6 + .../add_icon.imageset/Contents.json | 21 + App/Assets.xcassets/add_icon.imageset/add_icon.png | Bin 0 -> 716 bytes .../camera_black_ic.imageset/Contents.json | 21 + .../camera_black_ic.imageset/camera_black_ic.png | Bin 0 -> 721 bytes .../delete_ic.imageset/Contents.json | 21 + .../delete_ic.imageset/delete_ic.png | Bin 0 -> 977 bytes App/Assets.xcassets/icon.imageset/Contents.json | 21 + App/Assets.xcassets/icon.imageset/icon.png | Bin 0 -> 20748 bytes .../next_arrow_white.imageset/Contents.json | 21 + .../next_arrow_white.imageset/next_arrow_white.png | Bin 0 -> 357 bytes .../prev_arrow_white.imageset/Contents.json | 21 + .../prev_arrow_white.imageset/prev_arrow_white.png | Bin 0 -> 468 bytes App/Base.lproj/Main.storyboard | 1647 ++++++++++++++++++ App/Info.plist | 54 + App/Localizable.strings | 2 + App/SplashController.swift | 23 + App/commons/VCContainerFullScreen.swift | 19 + App/commons/VCHome.swift | 34 + App/commons/VCRoot.swift | 16 + App/commons/VTopLogo.swift | 37 + App/commons/VTopLogo.xib | 51 + App/createNew/CellChonMucChup.swift | 45 + App/createNew/CellMucChupWithCamera.swift | 53 + App/createNew/CellMucChupWithDelete.swift | 56 + App/createNew/VCChonMucChup.swift | 68 + App/createNew/VCConfirmMucDaChon.swift | 69 + App/createNew/VCNhapMucChup.swift | 111 ++ App/createNew/VCNhapTenCtruong.swift | 41 + App/createNew/VCNhapTenCty.swift | 35 + App/createNew/VCRootCreateNew.swift | 17 + App/createNew/VCSettingBienQC.swift | 29 + App/firstInput/VCInputId.swift | 80 + App/firstInput/VCNoiDungTraoDoi.swift | 23 + App/firstInput/VCSendTraoDoi.swift | 82 + App/listCtruong/CellCtruong.swift | 9 + App/listCtruong/VCListCtruong.swift | 50 + App/listCtruong/VCRootListCtruong.swift | 14 + App/model/CongTruong.swift | 12 + App/model/MucChup.swift | 23 + App/model/Session.swift | 28 + App/utils/SecurityUtils.swift | 13 + Morooka.xcodeproj/project.pbxproj | 1027 +++++++++++ .../project.xcworkspace/contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 12876 bytes .../xcschemes/xcschememanagement.plist | 5 + .../ptran.xcuserdatad/xcschemes/Moroka.xcscheme | 98 ++ .../xcschemes/xcschememanagement.plist | 22 + Morooka.xcworkspace/contents.xcworkspacedata | 10 + .../UserInterfaceState.xcuserstate | Bin 0 -> 22894 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 151 ++ .../xcschemes/xcschememanagement.plist | 5 + Podfile | 15 + Podfile.lock | 30 + Pods/Kingfisher/LICENSE | 22 + Pods/Kingfisher/README.md | 92 + Pods/Kingfisher/Sources/AnimatedImageView.swift | 355 ++++ Pods/Kingfisher/Sources/Box.swift | 16 + Pods/Kingfisher/Sources/CacheSerializer.swift | 87 + Pods/Kingfisher/Sources/Filter.swift | 145 ++ Pods/Kingfisher/Sources/Image.swift | 935 ++++++++++ Pods/Kingfisher/Sources/ImageCache.swift | 693 ++++++++ Pods/Kingfisher/Sources/ImageDownloader.swift | 532 ++++++ Pods/Kingfisher/Sources/ImagePrefetcher.swift | 269 +++ Pods/Kingfisher/Sources/ImageProcessor.swift | 397 +++++ Pods/Kingfisher/Sources/ImageTransition.swift | 128 ++ Pods/Kingfisher/Sources/ImageView+Kingfisher.swift | 292 ++++ Pods/Kingfisher/Sources/Indicator.swift | 191 +++ Pods/Kingfisher/Sources/Kingfisher.h | 37 + Pods/Kingfisher/Sources/Kingfisher.swift | 71 + Pods/Kingfisher/Sources/KingfisherManager.swift | 205 +++ .../Kingfisher/Sources/KingfisherOptionsInfo.swift | 296 ++++ Pods/Kingfisher/Sources/RequestModifier.swift | 53 + Pods/Kingfisher/Sources/Resource.swift | 74 + Pods/Kingfisher/Sources/String+MD5.swift | 292 ++++ Pods/Kingfisher/Sources/ThreadHelper.swift | 40 + Pods/Kingfisher/Sources/UIButton+Kingfisher.swift | 415 +++++ Pods/Manifest.lock | 30 + Pods/ObjectMapper/LICENSE | 8 + Pods/ObjectMapper/README-CN.md | 502 ++++++ .../Sources/CustomDateFormatTransform.swift | 40 + Pods/ObjectMapper/Sources/DataTransform.swift | 50 + .../Sources/DateFormatterTransform.swift | 54 + Pods/ObjectMapper/Sources/DateTransform.swift | 55 + .../ObjectMapper/Sources/DictionaryTransform.swift | 58 + Pods/ObjectMapper/Sources/EnumOperators.swift | 91 + Pods/ObjectMapper/Sources/EnumTransform.swift | 50 + Pods/ObjectMapper/Sources/FromJSON.swift | 181 ++ Pods/ObjectMapper/Sources/HexColorTransform.swift | 116 ++ .../Sources/ISO8601DateTransform.swift | 41 + Pods/ObjectMapper/Sources/ImmutableMappable.swift | 275 +++ Pods/ObjectMapper/Sources/Map.swift | 181 ++ Pods/ObjectMapper/Sources/MapError.swift | 68 + Pods/ObjectMapper/Sources/Mappable.swift | 139 ++ Pods/ObjectMapper/Sources/Mapper.swift | 433 +++++ .../Sources/NSDecimalNumberTransform.swift | 51 + Pods/ObjectMapper/Sources/Operators.swift | 377 +++++ Pods/ObjectMapper/Sources/ToJSON.swift | 179 ++ Pods/ObjectMapper/Sources/TransformOf.swift | 48 + Pods/ObjectMapper/Sources/TransformOperators.swift | 606 +++++++ Pods/ObjectMapper/Sources/TransformType.swift | 35 + Pods/ObjectMapper/Sources/URLTransform.swift | 65 + Pods/Pods.xcodeproj/project.pbxproj | 1784 ++++++++++++++++++++ .../xcschemes/Kingfisher.xcscheme | 60 + .../xcschemes/ObjectMapper.xcscheme | 60 + .../ptran.xcuserdatad/xcschemes/Pods-App.xcscheme | 71 + .../xcschemes/SCLAlertView.xcscheme | 60 + .../xcschemes/SwiftOverlays.xcscheme | 60 + .../xcschemes/SwiftyAttributes.xcscheme | 60 + .../xcschemes/SwiftyJSON.xcscheme | 60 + .../ptran.xcuserdatad/xcschemes/Toaster.xcscheme | 60 + .../xcschemes/xcschememanagement.plist | 108 ++ Pods/SCLAlertView/LICENCE | 19 + Pods/SCLAlertView/README.md | 271 +++ Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift | 1133 +++++++++++++ Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift | 41 + Pods/SwiftOverlays/LICENSE | 21 + Pods/SwiftOverlays/README.md | 122 ++ .../SwiftOverlays/SwiftOverlays.swift | 528 ++++++ Pods/SwiftyAttributes/LICENSE | 20 + Pods/SwiftyAttributes/README.md | 151 ++ .../Sources/common/Attribute+Sequence.swift | 41 + .../Sources/common/Attribute.swift | 330 ++++ .../Sources/common/AttributeName.swift | 181 ++ .../Sources/common/Ligatures.swift | 21 + .../NSAttributedString+SwiftyAttributes.swift | 361 ++++ ...SMutableAttributedString+SwiftyAttributes.swift | 92 + .../Sources/common/Operators.swift | 18 + .../Sources/common/String+SwiftyAttributes.swift | 257 +++ .../Sources/common/TextEffect.swift | 35 + .../Sources/common/VerticalGlyphForm.swift | 23 + .../Sources/common/WritingDirection.swift | 57 + Pods/SwiftyJSON/LICENSE | 21 + Pods/SwiftyJSON/README.md | 519 ++++++ Pods/SwiftyJSON/Source/SwiftyJSON.swift | 1485 ++++++++++++++++ Pods/Target Support Files/Kingfisher/Info.plist | 26 + .../Kingfisher/Kingfisher-dummy.m | 5 + .../Kingfisher/Kingfisher-prefix.pch | 12 + .../Kingfisher/Kingfisher-umbrella.h | 17 + .../Kingfisher/Kingfisher.modulemap | 6 + .../Kingfisher/Kingfisher.xcconfig | 12 + Pods/Target Support Files/ObjectMapper/Info.plist | 26 + .../ObjectMapper/ObjectMapper-dummy.m | 5 + .../ObjectMapper/ObjectMapper-prefix.pch | 12 + .../ObjectMapper/ObjectMapper-umbrella.h | 16 + .../ObjectMapper/ObjectMapper.modulemap | 6 + .../ObjectMapper/ObjectMapper.xcconfig | 11 + Pods/Target Support Files/Pods-App/Info.plist | 26 + .../Pods-App/Pods-App-acknowledgements.markdown | 153 ++ .../Pods-App/Pods-App-acknowledgements.plist | 221 +++ .../Target Support Files/Pods-App/Pods-App-dummy.m | 5 + .../Pods-App/Pods-App-frameworks.sh | 111 ++ .../Pods-App/Pods-App-resources.sh | 99 ++ .../Pods-App/Pods-App-umbrella.h | 16 + .../Pods-App/Pods-App.debug.xcconfig | 10 + .../Pods-App/Pods-App.modulemap | 6 + .../Pods-App/Pods-App.release.xcconfig | 10 + Pods/Target Support Files/SCLAlertView/Info.plist | 26 + .../SCLAlertView/SCLAlertView-dummy.m | 5 + .../SCLAlertView/SCLAlertView-prefix.pch | 12 + .../SCLAlertView/SCLAlertView-umbrella.h | 16 + .../SCLAlertView/SCLAlertView.modulemap | 6 + .../SCLAlertView/SCLAlertView.xcconfig | 10 + Pods/Target Support Files/SwiftOverlays/Info.plist | 26 + .../SwiftOverlays/SwiftOverlays-dummy.m | 5 + .../SwiftOverlays/SwiftOverlays-prefix.pch | 12 + .../SwiftOverlays/SwiftOverlays-umbrella.h | 16 + .../SwiftOverlays/SwiftOverlays.modulemap | 6 + .../SwiftOverlays/SwiftOverlays.xcconfig | 10 + .../SwiftyAttributes/Info.plist | 26 + .../SwiftyAttributes/SwiftyAttributes-dummy.m | 5 + .../SwiftyAttributes/SwiftyAttributes-prefix.pch | 12 + .../SwiftyAttributes/SwiftyAttributes-umbrella.h | 16 + .../SwiftyAttributes/SwiftyAttributes.modulemap | 6 + .../SwiftyAttributes/SwiftyAttributes.xcconfig | 10 + Pods/Target Support Files/SwiftyJSON/Info.plist | 26 + .../SwiftyJSON/SwiftyJSON-dummy.m | 5 + .../SwiftyJSON/SwiftyJSON-prefix.pch | 12 + .../SwiftyJSON/SwiftyJSON-umbrella.h | 16 + .../SwiftyJSON/SwiftyJSON.modulemap | 6 + .../SwiftyJSON/SwiftyJSON.xcconfig | 11 + Pods/Target Support Files/Toaster/Info.plist | 26 + Pods/Target Support Files/Toaster/Toaster-dummy.m | 5 + .../Toaster/Toaster-prefix.pch | 12 + .../Toaster/Toaster-umbrella.h | 17 + .../Target Support Files/Toaster/Toaster.modulemap | 6 + Pods/Target Support Files/Toaster/Toaster.xcconfig | 12 + Pods/Toaster/LICENSE | 13 + Pods/Toaster/README.md | 118 ++ Pods/Toaster/Sources/Toast.swift | 169 ++ Pods/Toaster/Sources/ToastCenter.swift | 75 + Pods/Toaster/Sources/ToastView.swift | 187 ++ Pods/Toaster/Sources/ToastWindow.swift | 155 ++ Pods/Toaster/Sources/Toaster.h | 21 + 212 files changed, 23944 insertions(+) create mode 100644 .idea/App.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/xcode.xml create mode 100644 App/AppDelegate.swift create mode 100644 App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-120.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-152.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-167.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-180.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-40.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-58.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-76.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-80.png create mode 100755 App/Assets.xcassets/AppIcon.appiconset/ic_launcher-87.png create mode 100644 App/Assets.xcassets/Contents.json create mode 100644 App/Assets.xcassets/add_icon.imageset/Contents.json create mode 100644 App/Assets.xcassets/add_icon.imageset/add_icon.png create mode 100644 App/Assets.xcassets/camera_black_ic.imageset/Contents.json create mode 100644 App/Assets.xcassets/camera_black_ic.imageset/camera_black_ic.png create mode 100644 App/Assets.xcassets/delete_ic.imageset/Contents.json create mode 100644 App/Assets.xcassets/delete_ic.imageset/delete_ic.png create mode 100644 App/Assets.xcassets/icon.imageset/Contents.json create mode 100755 App/Assets.xcassets/icon.imageset/icon.png create mode 100644 App/Assets.xcassets/next_arrow_white.imageset/Contents.json create mode 100644 App/Assets.xcassets/next_arrow_white.imageset/next_arrow_white.png create mode 100644 App/Assets.xcassets/prev_arrow_white.imageset/Contents.json create mode 100644 App/Assets.xcassets/prev_arrow_white.imageset/prev_arrow_white.png create mode 100644 App/Base.lproj/Main.storyboard create mode 100644 App/Info.plist create mode 100644 App/Localizable.strings create mode 100644 App/SplashController.swift create mode 100644 App/commons/VCContainerFullScreen.swift create mode 100644 App/commons/VCHome.swift create mode 100644 App/commons/VCRoot.swift create mode 100644 App/commons/VTopLogo.swift create mode 100644 App/commons/VTopLogo.xib create mode 100644 App/createNew/CellChonMucChup.swift create mode 100644 App/createNew/CellMucChupWithCamera.swift create mode 100644 App/createNew/CellMucChupWithDelete.swift create mode 100644 App/createNew/VCChonMucChup.swift create mode 100644 App/createNew/VCConfirmMucDaChon.swift create mode 100644 App/createNew/VCNhapMucChup.swift create mode 100644 App/createNew/VCNhapTenCtruong.swift create mode 100644 App/createNew/VCNhapTenCty.swift create mode 100644 App/createNew/VCRootCreateNew.swift create mode 100644 App/createNew/VCSettingBienQC.swift create mode 100644 App/firstInput/VCInputId.swift create mode 100644 App/firstInput/VCNoiDungTraoDoi.swift create mode 100644 App/firstInput/VCSendTraoDoi.swift create mode 100644 App/listCtruong/CellCtruong.swift create mode 100644 App/listCtruong/VCListCtruong.swift create mode 100644 App/listCtruong/VCRootListCtruong.swift create mode 100644 App/model/CongTruong.swift create mode 100644 App/model/MucChup.swift create mode 100644 App/model/Session.swift create mode 100644 App/utils/SecurityUtils.swift create mode 100644 Morooka.xcodeproj/project.pbxproj create mode 100644 Morooka.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Morooka.xcodeproj/project.xcworkspace/xcuserdata/ptran.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 Morooka.xcodeproj/project.xcworkspace/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Moroka.xcscheme create mode 100644 Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Morooka.xcworkspace/contents.xcworkspacedata create mode 100644 Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Podfile create mode 100644 Podfile.lock create mode 100644 Pods/Kingfisher/LICENSE create mode 100644 Pods/Kingfisher/README.md create mode 100755 Pods/Kingfisher/Sources/AnimatedImageView.swift create mode 100644 Pods/Kingfisher/Sources/Box.swift create mode 100644 Pods/Kingfisher/Sources/CacheSerializer.swift create mode 100644 Pods/Kingfisher/Sources/Filter.swift create mode 100755 Pods/Kingfisher/Sources/Image.swift create mode 100755 Pods/Kingfisher/Sources/ImageCache.swift create mode 100755 Pods/Kingfisher/Sources/ImageDownloader.swift create mode 100755 Pods/Kingfisher/Sources/ImagePrefetcher.swift create mode 100644 Pods/Kingfisher/Sources/ImageProcessor.swift create mode 100755 Pods/Kingfisher/Sources/ImageTransition.swift create mode 100755 Pods/Kingfisher/Sources/ImageView+Kingfisher.swift create mode 100644 Pods/Kingfisher/Sources/Indicator.swift create mode 100644 Pods/Kingfisher/Sources/Kingfisher.h create mode 100644 Pods/Kingfisher/Sources/Kingfisher.swift create mode 100755 Pods/Kingfisher/Sources/KingfisherManager.swift create mode 100755 Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift create mode 100644 Pods/Kingfisher/Sources/RequestModifier.swift create mode 100755 Pods/Kingfisher/Sources/Resource.swift create mode 100755 Pods/Kingfisher/Sources/String+MD5.swift create mode 100755 Pods/Kingfisher/Sources/ThreadHelper.swift create mode 100755 Pods/Kingfisher/Sources/UIButton+Kingfisher.swift create mode 100644 Pods/Manifest.lock create mode 100644 Pods/ObjectMapper/LICENSE create mode 100644 Pods/ObjectMapper/README-CN.md create mode 100644 Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift create mode 100644 Pods/ObjectMapper/Sources/DataTransform.swift create mode 100644 Pods/ObjectMapper/Sources/DateFormatterTransform.swift create mode 100644 Pods/ObjectMapper/Sources/DateTransform.swift create mode 100644 Pods/ObjectMapper/Sources/DictionaryTransform.swift create mode 100644 Pods/ObjectMapper/Sources/EnumOperators.swift create mode 100644 Pods/ObjectMapper/Sources/EnumTransform.swift create mode 100755 Pods/ObjectMapper/Sources/FromJSON.swift create mode 100644 Pods/ObjectMapper/Sources/HexColorTransform.swift create mode 100644 Pods/ObjectMapper/Sources/ISO8601DateTransform.swift create mode 100644 Pods/ObjectMapper/Sources/ImmutableMappable.swift create mode 100644 Pods/ObjectMapper/Sources/Map.swift create mode 100644 Pods/ObjectMapper/Sources/MapError.swift create mode 100644 Pods/ObjectMapper/Sources/Mappable.swift create mode 100755 Pods/ObjectMapper/Sources/Mapper.swift create mode 100644 Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift create mode 100755 Pods/ObjectMapper/Sources/Operators.swift create mode 100644 Pods/ObjectMapper/Sources/ToJSON.swift create mode 100644 Pods/ObjectMapper/Sources/TransformOf.swift create mode 100644 Pods/ObjectMapper/Sources/TransformOperators.swift create mode 100644 Pods/ObjectMapper/Sources/TransformType.swift create mode 100644 Pods/ObjectMapper/Sources/URLTransform.swift create mode 100644 Pods/Pods.xcodeproj/project.pbxproj create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Kingfisher.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/ObjectMapper.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Pods-App.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SCLAlertView.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftOverlays.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyAttributes.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyJSON.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Toaster.xcscheme create mode 100644 Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Pods/SCLAlertView/LICENCE create mode 100644 Pods/SCLAlertView/README.md create mode 100644 Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift create mode 100644 Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift create mode 100644 Pods/SwiftOverlays/LICENSE create mode 100644 Pods/SwiftOverlays/README.md create mode 100644 Pods/SwiftOverlays/SwiftOverlays/SwiftOverlays.swift create mode 100644 Pods/SwiftyAttributes/LICENSE create mode 100644 Pods/SwiftyAttributes/README.md create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute+Sequence.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/AttributeName.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Ligatures.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSAttributedString+SwiftyAttributes.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSMutableAttributedString+SwiftyAttributes.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Operators.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/String+SwiftyAttributes.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/TextEffect.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/VerticalGlyphForm.swift create mode 100644 Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/WritingDirection.swift create mode 100644 Pods/SwiftyJSON/LICENSE create mode 100644 Pods/SwiftyJSON/README.md create mode 100644 Pods/SwiftyJSON/Source/SwiftyJSON.swift create mode 100644 Pods/Target Support Files/Kingfisher/Info.plist create mode 100644 Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m create mode 100644 Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch create mode 100644 Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h create mode 100644 Pods/Target Support Files/Kingfisher/Kingfisher.modulemap create mode 100644 Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig create mode 100644 Pods/Target Support Files/ObjectMapper/Info.plist create mode 100644 Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m create mode 100644 Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch create mode 100644 Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h create mode 100644 Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap create mode 100644 Pods/Target Support Files/ObjectMapper/ObjectMapper.xcconfig create mode 100644 Pods/Target Support Files/Pods-App/Info.plist create mode 100644 Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.markdown create mode 100644 Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.plist create mode 100644 Pods/Target Support Files/Pods-App/Pods-App-dummy.m create mode 100755 Pods/Target Support Files/Pods-App/Pods-App-frameworks.sh create mode 100755 Pods/Target Support Files/Pods-App/Pods-App-resources.sh create mode 100644 Pods/Target Support Files/Pods-App/Pods-App-umbrella.h create mode 100644 Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig create mode 100644 Pods/Target Support Files/Pods-App/Pods-App.modulemap create mode 100644 Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig create mode 100644 Pods/Target Support Files/SCLAlertView/Info.plist create mode 100644 Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m create mode 100644 Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch create mode 100644 Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h create mode 100644 Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap create mode 100644 Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig create mode 100644 Pods/Target Support Files/SwiftOverlays/Info.plist create mode 100644 Pods/Target Support Files/SwiftOverlays/SwiftOverlays-dummy.m create mode 100644 Pods/Target Support Files/SwiftOverlays/SwiftOverlays-prefix.pch create mode 100644 Pods/Target Support Files/SwiftOverlays/SwiftOverlays-umbrella.h create mode 100644 Pods/Target Support Files/SwiftOverlays/SwiftOverlays.modulemap create mode 100644 Pods/Target Support Files/SwiftOverlays/SwiftOverlays.xcconfig create mode 100644 Pods/Target Support Files/SwiftyAttributes/Info.plist create mode 100644 Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-dummy.m create mode 100644 Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-prefix.pch create mode 100644 Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-umbrella.h create mode 100644 Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.modulemap create mode 100644 Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.xcconfig create mode 100644 Pods/Target Support Files/SwiftyJSON/Info.plist create mode 100644 Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m create mode 100644 Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch create mode 100644 Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h create mode 100644 Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap create mode 100644 Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig create mode 100644 Pods/Target Support Files/Toaster/Info.plist create mode 100644 Pods/Target Support Files/Toaster/Toaster-dummy.m create mode 100644 Pods/Target Support Files/Toaster/Toaster-prefix.pch create mode 100644 Pods/Target Support Files/Toaster/Toaster-umbrella.h create mode 100644 Pods/Target Support Files/Toaster/Toaster.modulemap create mode 100644 Pods/Target Support Files/Toaster/Toaster.xcconfig create mode 100644 Pods/Toaster/LICENSE create mode 100644 Pods/Toaster/README.md create mode 100644 Pods/Toaster/Sources/Toast.swift create mode 100644 Pods/Toaster/Sources/ToastCenter.swift create mode 100644 Pods/Toaster/Sources/ToastView.swift create mode 100644 Pods/Toaster/Sources/ToastWindow.swift create mode 100644 Pods/Toaster/Sources/Toaster.h diff --git a/.idea/App.iml b/.idea/App.iml new file mode 100644 index 0000000..74121dc --- /dev/null +++ b/.idea/App.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..8d66637 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e1f8477 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + Objective-C + + + RELAX NG + + + Spelling + + + Swift + + + TypeScript + + + + + Objective-C + + + + + + + + + + 1.7 + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..cc95f4e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/xcode.xml b/.idea/xcode.xml new file mode 100644 index 0000000..46d81b7 --- /dev/null +++ b/.idea/xcode.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/App/AppDelegate.swift b/App/AppDelegate.swift new file mode 100644 index 0000000..ce8a14c --- /dev/null +++ b/App/AppDelegate.swift @@ -0,0 +1,40 @@ +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + static func getAppDelegate() -> AppDelegate { + return UIApplication.shared.delegate as! AppDelegate + } + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..46c6661 --- /dev/null +++ b/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,106 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "ic_launcher-40.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "ic_launcher-58.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "ic_launcher-87.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "ic_launcher-80.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "ic_launcher-120.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "ic_launcher-120.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "ic_launcher-180.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "ic_launcher-40.png", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "ic_launcher-58.png", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "ic_launcher-80.png", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "filename" : "ic_launcher-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "ic_launcher-152.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "ic_launcher-167.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-120.png b/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-120.png new file mode 100755 index 0000000000000000000000000000000000000000..7512e3f6318d4fd33d6c8d4141c821e15f61080c GIT binary patch literal 20748 zcmV)QK(xP!P);1Dnv zY}_!mWm}e9WVxtIqh3bSd%eA!`mepunIdC@9b}O6>%jGlX71d3&t7}2^?j>t;No@h zx_Di@E?yU}i`T{L;&t)5cwM|MUKg+b|6IcV;(Jq?o160&uUJuDTT)SMrLA(mB&zHe zX-%^QRqF4HjP%d*=Gmn$L2)=zdbJh;^oVi+;;WmRW)M4vZ9E$DHKz zhAhGcGO~fR*G?rv*5G(<_DqKqd9LNmv6r{+*xu69)04g^;0yF>Y-}uCv+A;Uub;E> z&z84_XXKsGeK|b>#Bmcs)_`Ey{CT!W|50VwVHues3&RW5R8KS7p6J+f-*2}5=0~Sb zpB`mTyC~ob=aR3z>e?mOF1-BjHek-i@~7g8=R}$WDnqbIPJ-axsx|c&lRp<>iW-ax z1tXgattb2^cJ4TR;A{8(`raM%OYQBxFw*~qO}b{y+STvAeDk-iYcE@0_CQopdMt>x zU!p+E9L#cYf)dh+_7)wY8qQDBwTwwP?|z zId`nP<)1d4D!r`e$)qI3No&cv+L8s+BS0&cp{>q?wyqS~WyLU; zpA@ur);IP*!%rwp0g=AXHiknP`=)KbEVuxBxJK>OQg@>Yl@8Ui+%8t@SKkwCu9H-O(~BsqvT5^XOJSG#7GN zfNLiv#YF!b(_o&BK^RE$SGS84hz+@ri~Rh#BGY6=fO^{;XipD7dSaOSV{t5thMt1J zjIydL>BSzpDB#~OO#gM)UFWMPuBZ@G_QJ~i!peZxSC&m>^A)c;I3A0Qji)262>DtD z#s&r+c;JC3UL%A!XU?2pjlXnh;H(y4T-Opz7?+m9b7}cG&$Bf8`6~l3etHzr-Z4m{ z7N@^f$cC}B5cY@XkPj}06qFsKT|oZ&wiz&99DzdbEa(CPr!}Fbv1Bzp!Ap)e-e z#~pX%(p4BA9WB_8OAGENEiCXLQe<15f!I)YaeUvL&(8Yw^9Of7|H8Iy zUDFq4X7$Y65+g7>obb`&CSlPR>4rK;0ojQ^krb`03H`Bl2%p~vUw4-2DQTtP1gOSI z(4B+OUK)V*sRi(SVm?Hfd@=E5X?Y3k=6nb*jq~TqopH?Yl-3j#6;+TQK095o`T6<5 z8#djrXx@xjmsV<_D~mHZb8_=N^i!|o5k#Z}3sFy2AN8dAM&>mhUo>a_BYRID+xhs3 zhuU!bj}t`sV+dGSw{G3ao7dg=k)_$Hs~dJ{rM{zSq9r2`Oq-)tA}Eka3%#@;0Z|f5 z3xv}7P5GNTmK0x7ySD6w%Dkc<{__4`zCf?Vy>eckE!#6s1jr7>^SQkMd{blaMP^Nip+;8m+Fd zX6~}0HFH+g9W3w#es=fqyYF?~lKr+7@WO=)bLY&Mv;N(4H+*;19)GF-#keHLEm{DJ z(-lV`uE%5?l>RMP5-q$1Wh5=k*(2uG&MUoc;f8l!l5?BdvTe`v-|Fb-cxH4YS|3!r zAv>L5))A=80gAm&fP=-ml%_E|gsb2nUn`J7v3i91j2$plnSFqoA@L9;j!unPT<{EA}@*ea9Bx1Sq@ zW$5^+`5d0L&vNse@g#7{is1ph1s*}cUrTE--)e;Ld_Sx^$kY=r78zwO^1@bWvt?|q zUvktE%8Lp^jg5_SH(z?~Uu`g(-qrYs?p2Qu(`IEz-Px#?NcTfv&8Ceaut+Gm2|;%^ zFKrZ=?k9ZA6nwp7fluGQY17t?GxL63QZTq}+qRRh0p~1VwEU))H^1xJn}!;fR&Gywl>v=h49h;! z=O%487Ulz&6hfRyrkf^}e+Zy=>J3&|9@b~6B1aqoz5!`0QxtTh|Ib>hT{ zulDrxyzsUW@Uq3rHr>>;{yU4GCXaS7%45AuvkU@5`n4sHJ~|Jcr6mx2Bxp%+s0x=A zX#;7c)ssZ#SEnK0a~evQ0fDqziIOjTEUK>S2`nyn|1I-9hgHeR2$6ujbQ`ks9_%tl z$W@Q0NPrgixhkopPmuA7(M_gWLReIwnYK%&=(Q6%^uahix0nl9@RBu9CD(7W1hOgK zaldI7BOu15CSVi-9V zS4~M*;oq0RqOFj91I8pe5vE0_n9!w!2F1`FpG;5k3o7cBVx(+?2)Br8F~r|@%z^%yCMf^98lG#aA?A7@D)jm3 zM#y>4S9;LqmC3TmwIO&v+6?m>%b+a}z$WV;&_)vtFY=#`idUy*FaE#{?^^#>DBSrL zuzXqbnhnjvc^mSc9+yPJc?%;%Cir8s;Q8!A$Q41_{G#iJotP4r!t}fcX^lueRlTYP z(l=JZ_+UNJDAxp&HSj(;3gy`$IImAyVq5NOxPPu&%p=0lC7XmQp+PDplrEz{@Szlb zZZVwqcK(!P?}8*|0tg6N?(ga0IT(lUfnG>)=3+!>i}Qf5ErI&U`6RqB7wW`w&wWg! zBWV>B37#8gLj2k)=<@>Z<|IuX(^0qE&RgiO{ooZ>TruN<7I0ZvS#ERD%xkI-N@enh z#i^?;nlNsyfb!{ukjnfnyqX9j(lgTJ!+H`hkBq`RHU_hozR#GBb)C>nft~ZV7MXmLEQc|e zgh9U7Ow((-6(kcmX@(P&)nvWCvJ_f@On=5ux`>>kDJ+eYEn2l;#Vr@K4=yh*pI4m? zUy|43_)~UgT9pFjFXllEs_vb-Wi#}}=uE))HMyNgIw24e78NvK0sWZzj>#GYOlSPr{7jPf!AUb+KqDjV1 zBHS+xKpa%xs)dW#tu%1S;6-m_#+q4b@P{r%okx9MfzL(V~21Ka|Hi$@E!HQh+_D zH3IvkUg%Gqh55J3pK>s4JXHGc+tkv!r$AWDr9jmtgzJ zG)Gl@z`mS-Faa%1B|*<7)9h@7>*((kqDl%0+fYfYL3b(G&WjPS(^>jao_tNMDFESb z7eSg^!0`gR2b(CJabgtuf3`r`!jQkk0UEE!vt>J*f&?z# z)&Ohg7^Gt{jvbWNG@68ps^;eAx}!&rUYG)IsGZSRGT`+Jsc{yYf?eT*bR!8!nR**; zmirRWKJ_d-zoEsE40K7|I4h6ApkMlkPL-z%P`RTR;*l4T`4lbChvoozq<1AbP(QaA zna&724|YP<+~CG=8dhBAH^UZQ3aiiyX+uOUdOhD{tVrP=$@wfEOvR9leELm^*NI<~ zC9wOmE`OzsN7B@)NGfEO<>jSL`jKr$(sKQ=4%lBl451^5ZgCvL_7HS3`KsVSz8*yB z$pR>Uvj^Eo7M>5zrA?2Ue!nf-giL?*sH2# z0Zn#dzVv^qlmt){aI!T#P2}$SC)=RDz!@n{i~SfAqiC~-5u@kKMLve45u^p$(W5gU ze*XwG*@XJh1rTU~B-;JTrxro$h(p+&Caz4Tlr$JoZ7SyHAs<{Ggnc$L6{h@MR|##z zJV(Gb`Cog8D3&R3A87>13Vm!gyvs^C)aQVVtcCv2X^3BKLAEQ7$E5@Ksl6K=7=o9! zkw%EEObFr{aUMPxy$m^bpM^265Xxnhlb?s>q3v3RsL6brEP`i39!M$qmEq!ZeA$Hr zvP41jvCy92hU)A9X%X{>)98%N!g|jQKqj&uAy}%38fKaGIAFJA@5Om6r8=(y z7G!bIkXfY2N$K)rg6aAz_wLLw^3QDv4qgqxL>_@0KOx>xD}&?aAadw`MMe-gmW()3 z%sUaNBEa-7q{)w*fvpk0^^i{`0Av0+`7st!Oc7Ei1TNT9bM!)8c- zK@rh1)$zAu^q$>G7QlkODgf!ziy#*H9l}MgZ#>Zn;Y)|$X=kQd!T_S^u}OgRJHM@< z*{Xz(csE;tF*}0}a~NWlKI2Oxbo0|b3yZt6n7r*!IreXh(0n4BKCkJ-^D5x+c&snx z)h%0e1w5XCHI{MKazbNReU!q$i^LbvWK@EHkHjg1v+$BF^XR+tH%@p@9;W-g^90Pj zBV^752PNM&3;LQO4(O~SV_ds9wINe#)djeDb+U~1Zz}~4o0>2sn*%4NQQW=uYY>KY z`dfO=$L2z=2|I}qi!}Ebz4v#ILg_KxEEt(=OLB$JJ}LXon4-a4bTUWWOqdg&f&ZFl zoj9JQzjv&XElSW!yhbva9zUNMrt>P`vt8|{$NYK>IjZy011VT%#u-+byg4Dy2Wv)% zm)I!ed&b2i()4|)kcaEVWyq3w8;~MMiCOqrrpR3qktmO6Ukds+_QQ%fWi@hv7vle2 z2y<2t;)xNM196Ai(1J>Y-0kWBN3^eJo_V!&z6T0%ZN|5lsWgucLP)1!-ay=aV}r9H z+4G3sn%^hXzdz#Um7UGU-b-*(k-Vr663;!e@A=kbNS`*GS)a!#pIZ$EJC=pi79%#` z$kOsk$kgOy4;?(%bHNI@y{r9LyENXWRr?EZmJ4L3VR@4Sq`I6LB1J) z-;mHIcH<^t5o*jr+$t?b7yY}?_8`~Jo#sbLJ6IvybK}r|eu{Xn!Pit;QwHnydPp(S z#ulf#mH)gV2X=Fw69auMmu5R6wie{lrc<3oXH;wV5X_~ykUvS>-Rl(3+eDd+pB#hw z>md@zlpZqHQ!0c}L7S7kON=(>4xB*(SzZKKB8D(qDj`Hq!1MYlxB*aJHe?(gfqgXY zSR+z@vrFuyK)mZT0YhQh zE7@G@=ULVG8|g}1t<)o5$iaigA#QorDzorKc>~s?2^P5#`KrpPcc%?wKGX)WBgOFw zQF|m7`f)gN`amX=>AhehS)->$6J6TC)`8~on+gtzHOjC-(BDG#C&wXvaRpB#Fdiup z=QKaN0NSY}JgrI88a|YfFJb`+iB(##ZUczZ;?$U84&<*DGHsdvJPy|A=Cl=&Lw*@79O?-v!2ixjw@H4hFDqV_@FR0m0soPf+o z2IhBL;O*C4b|6d-nImZD;1;aG2B`^SQWlJ?vCp4NU@>eEO(gSgzkeoVvM9WwgfNGG za2V1fy#&5Qem`chi1ym#*wMCA55JM3!#8uC_qu2z>Eq2CRy5WI%jWryB}E3Dgzgl~ z8b94^7^3R>r}7+_bq*xbKxP%h89~}G z5nebamgP7mH{^k_tpjqG_Sl*J`{gV|Xf`hBON z{HPUj%AWMQ=YA@I14O^+L4g@Uv6YJgE65SGRZL*<^YfwJG=pc#*oH9s6EOdNH@qz= z?#peWbc3tCcrv&3u_t$LzjJ74D09J@Xm-V7v9ZTr-gaM0!+4)D!_UKg;s7o1zaJz3 zGcu`=OluS}TjpO85-xWa-^TBufKH|vy1TfsI#w~ac3rq#SCu58+Wr&_5-=_)hmh;x0%j>Rac&`ud3iAR5Ae{U@VcQ1 zVcKd6JRK+w>d;6_qy>c#6rx7-&|V%O^iF2=7pGu-_YmY~`pI|NPF3YJAI#R*>Zj)& zB2bmmd7qL(bV+H_H7|xMm_~^s3&h`eoa|j#q7Y* ztR(5gbNe)82VxK!Ly(I7j>RRRNVPH7s)*@<*60llIT$sR= zxxEL{-EENWYlHgSAmr8zA!8s-oHiSzg(o+d_0z&KbW8wHCCj2PrPXhhrhv~AEg~Et{7%;5M#IRLvOtACI{@n|OW-B5Eaf;f&x~fEKTLxCDhW8^@OsND zV*HwrwY|Fgse89R__fy7){(bD5N}y;Qg?S-%J#^8v2wAYxu&s3Ih7DtO_J0efqpg$ zG!i{24>%^1RdSGkg%w56S%ClK7(~tCI|6>U*%qQ2%+WOLC%Ym2tPRRp(n>=|f4zyb zD+C-^6vw{V+Sz$!xrGJDn8jK$Q!p^pxAgjzSHJ6ugN3U1 zu@M3*7N;lKd88@-yc{B#Y{@52Y6)xWWc66Wf49POGzm$wU+wJ9d0U3OS$PJFVWk2V zx@PETnHj~AKp#%^wV|uG2Z=-inwEu0zL#woRZ)-=2%xmM1T!k@uw1Igg0_6rpHz{< z+@seat1MT`eLn2hd~g0N0(eG=4E_DJP(MDOGX&;}c>zFQ3i_Rgq5RhwC~>Foo|)_9 zB|*IWPQrf&cRbS#<)4IMjkB7OA0soufCwlSQiQrbUMz|!1BiyE)J;>u<9 z!SZ<}T3}HyDOcyEr2JFb@vQ6a+WfZ%;rb=!%WO1`99E8^QB9i6b2+(B9sTY&Hwa za>J&ALxuQK3-5b$2{qjP@OKl|S^QFbCWq2$t>d~VE zQX(-PG7gN%>Ai>Aj_tVr7r!7bh~%F{z~$w$3)Wt?_}cXgFaPj@vAhK(tq6qML`fOS zLX2w=G^f#;=eXUSE7}u9?k?OB$Y&K0FuI`v>OU-pT$01V7f%AP=+0;s`nGP^KRO0= zZv=8yc7-&hQMw0cUk*Gyw+9a&-HubIPa&O7@mo`66{^<*Symtk9$x6i=#k12?lNhe zo<%a5WXPYiT19nDH8w6-i_Jr`(X>;BKW_3S+t^{@tf;I?ZkSAOG~mf|Hv4~>c*KJWw+>3sfS8Q?I82byIb7!`yVYke>) zRMBO_2q3&VjtRD@>Z(%IMIs!KLu-Q|~H#j?4Wzn~s-nre`r zSAfWf1fMS-hM^!q)+Vhd$S3>Z_xllx#?Uj^hv9Gr6*Fs5+ND4yQ?8eI_~ZcLZx=#+ z&uqx0IZky4tH~NCe)~(}3wu3WyXZdYM-?l(!5nAz?u?BGiZ0Sc1*v>>z^IIz`Fiu8^m?kv?yk840d}Q zXeEDpbQsd00p*u%Fb?O`D(vKk9=A+IQbs5+pKVFi8fAk4dtl3<4+#7K+7 zUg(R-)yyG2J+lC)@$pUvgcM$p!fL0P8)7^g=xBGe7_ zOcJy1r~P-+oZF2RhJEVPslV>;?@yg?6D?eR`MR~Ax#E5Itli=%^B#)x^c|O(N?>Va zF06OXg!&HRe)%3Jw>r(lz6Nb{OQBul!Ke{S+Ow40VY<;hmbgndJqVfq9k+)nhWNmxhfY|T|bwHMU(W}y1@Vy zEhY;`lBNudByjvxCkn}`v>b22ftG#H^f>wMQS#dcA?^%E-bKa5xb2eb@xk3QQFGFV z%wpp2zg){*2#d;PA2|c@z$kJLs_CNc_ zO(!ar`FF+y(RA{jx`!6;wn|7}UIuj?`A$~(C^!)#+jcyyAevsk`3Q!Wp&)M_ z!d@>bNX zwTMzBQ}o^*8&$LF(Rj=YkwBj*k~xmjbBO}URY7>(I|s_GGoh?0r+T3s>Z}4ttjtFe zCd)-#W8_#QGT(MW=YbBeM~oOjG{b){u>$xag3V;QSve2_vXhK(WAvydQ_Zw(>#b0vc>J2xHjt*_#yLa!vA1vUtYuDy2EuVcu*%R?%IpOX|Hm>OM zBB-BPN`eSX?pB^&BcEdI9fI{#2ZZMaN#n@OrgX@6T~0Sm07hbk=*+$_;b+M*oOCK#`WA`Uae6Nz;}PC6;_;P|uO;D1K6pcZda8`U z4Ege|2mz)PX(4GPnPr6*)gaA{Ct{@8NyO6;^pXZEs)6$AFuYzBWu+x(KO5t(%e2`` zrk~NM{U;BhW5IkB^${SXCADG}FXXq~yS6g4d1xb}FU{lN%%55(qYzpnysAY?Sv)ew zgEm~kM2iTBT44{gxn$C>tA@0`lDEMKE-DxOGCbGSBQru={&V{wB`n^MUU1YD=QS6v znm23S5)$y1KUlz3RaLds5#NGPk1b3H(PDikgyXFWgUaV1Z;d6loaQaHSi+8AP>VsL(no=;>#p}tcI@MZnQQ>(UhBy5SdJ4 z-!P0NMX+>YKwOC9l-!SY;+iol1CJo1R0v{$Jp+YC z9%MLawFxgJ5popzb)Iz+_7cGIFls>pUz87<%(*h8AzCP+%{zuzJkH-&mc69CGx^sX zW-bL2S)w#!qqJbzEK#B`DGLKQ1YXE&C(|){9!Sum7#ZtDIztHT0y!S! zhQf$OqR<&KW1Wh$F)|qqZTbMB`MC&>(uUsAkL+*)(vCsM9klt9Zc&{z5nG$qdTLK4 z8Pif0r^POeTM53|i&4PCRKB!~S=+P7rpWru%;Ph@COvpA@uLKQ;ldzaao!TOh?>+-HBBM_l!dHcd zaldhpn1GEXayj-JeaOPVC2}>sCf|Y;W|RBT?5VCkM!Hy|5dDoz)=puK*`1?YaO}CU zJR7ZvKGHse?{d{!1Yb@uWPwGdXhWD9*U<6N9s*KXNJ|jByauu(Dd(3`1?gqshlum)@^T-_S&o_d3Mn`H^ZVc#*rb& z#BG_D9}{}o21sFKO&l8+;f z>?PE#kgv^THD?$M@oAYg!O0F1W*diX;54CYBOr17!8!s(OK=etUcV6FH7?$B$rOUV z>$76qlI6nd81FQFf|hX(&L)c!*rsPdtZb^~l=X6zNFf3&$p3>)bTS^Vkh3nFVB3k+ zRTcTBrSTjMlzPduW}U$*Gf{>wc&^21VF+y`%zLc;WR{%zXq}A~g3jY!Zzhb!B10_d zlgy_0f;)7>(Jo7Ln#g4-g|rbN?MomKVx35&AB6gY1lW$W9LB&v4++SyA-a)mILM( z(qxGQfo&8$NnnZnn-%m$>1&2GmbfhW-Q1v`ghUjJ7C{hcBiLEq6eqDBBl4LA1gI=p ztnslZg8m%ZAYL-bOt>Dh3MvOjwq;MYl`?cK{MP4ulO@cPep7-9zNSM<+H7+ z7{2W`kK4jzNSgP%c8rENOh0e0mPg8H+M2DN?vA zNTxD;Q+dd2drimZy1VUvSk@*?Zdl0{CVP-w1VT(ghN zs-tPYx6S4Ee>znlV z;}t{%*4`=e3PfQl%=6lTQ(-FX#Q>JAIEjovNE;WC8V zo&I8NNiM9W0F*;x5ZcH^#B{fgjD>31S5`ZxOp63N7ARo*aJAHo8nuX9y=6j;BQn9@ z+N(GFAzxR=UCwliZk;5K-JNm+D;DcvLPxTlMX}<4uz>r<2T%4@CI)8Yd&5%Fc?0Dr zX-f+s)a9j9ibtqZ{dhAp+M4PQPvO>)71%5+VYQF&lD}c|s!W#?stkor7PgyCg(LGV zUK4t`2Nnz5ud0GDzW_q5A7*danqz9~dBR+y|vK!&_)XO*m)b?F2i>wFCH& zc?IDWXP$^kE{7G#v(%@yj5zxj7D1Ys?^-bT@0b<-O&jE>?o1q@^&PJg?2dT%v~+8YKf2+=S*57k^HddQww0MC`R_tcF3#>gLTj) zb$FQXWI|tQd zYLA^d0fCpokY)#bBoqlguR$nTCE=K;s4OMGB%-&UP&I9+WXdFyoJJ}mI8`15m`X~D zQB_yPN4eA1;zAvdB={&qQ%mry5;M=`fYfMj@yuyV|4e&5u$EM*aWUGQQ(zRrv)|W%xJOgq~zzrU` zk|Q&tKh*_k+aT{@;>CT1D!M9CgFXF&51$v1wQJX|ECHHYp!!nCG4JCju?7!p0x1t%B80n%HWIG^m3Jm-|FpBq1mS{yZVrY8YveP1qrnK(s1Oo5hk80&N*+ zsWB3AlGCa3@^UQmG?4#g1JDc@OLL*^rOkb080w;OC{1~E?`}tfJ(UKOJhX{f?Ke9R zWU|G|&AAEIxu{ZbdW$EE=NX9HOPlRWd*L}4;h|4XU~)zDtqTslc>IY6cJAEyh7JmO zBe4dPOjTzj6Wd$9cJ}3Ec21F}pO7UR970q?e6b(deFNNwBA56a3EW9`i*7IilV3%5 zSjWV_IrE~OYNZUGS~NRJYv}d#d)tux{!wU8oZ<-$?=8-#KsK&OI5du;m>=Vf3OZ7K zWSWy)$PR50;3bV?KNCoJqU`0!nDbfsDk$VmJmd;|^zf7B1i~)si?rNq8HIk=Q3#*g4etrf856}ENDYzk>|dD z*MI-Vf4X19+)c%pf4{3 z=DPv&LO<^*t}5B zjuT^GZme}2!v0Z+FOX&Eh!aq=Cc&9rAftCv*!X$u#g<<^dhdVXO`HJpW@@smgGUcO zoQbFEhSps9mzzS>c{$IILt$AUHu`UzfdA7&eD;$-KGq5fyuk~85{~Sc7$#sVX^}5u z{)_~jvYhxUtJ9+WXsHAmudRUc7YiWGE8z34CaNEp^%Kkd(3->W9FAkb*<9S_Uy81p zK{i={%yEX(nM6EJ)Ja25s1T}OfoAAPj*gs6j%nVFe1AX^Et6M#eF8^clq4 zb|XW=W%XVj^6Sk_bMem5rI@?L1BHCI;Ump#8-q!xS(=;AH;O!Q8Qbp6F`ANlgCbk$|m&!z%7C&<^OY-d`m z{i^Dm$wCkNoR&+t86pN8SQu7YL_7Y&%}{Td$tT-Q_-2cbTRg-Y{(>0%5v=RJ0SR z-s4D;X*S8UvS9$>a2{4IT!wcRUWTh4@WYpoCM#9gJZPOTl=&{wvPyS$hd_4Nc=uI+)hKBkRigh4WCilcDCB>W>DwJw-JPI>Ej7+jzs%1|B zD>snA;=q#=@7RsdKXjrU+O@=czqFJeZ6oHWE)b!YiW7Btx)b(yk3#(ES*T~&T&$_h zBhlK_CUOxb!Hwl=h*(Lai3%A82``o8sljX}fpmNfiSd5KN4pUlI)li-am2b>5F_Dc zl4H)$Z?ZDX=U%nsGQ8VbiIqEp2#yNpr~=b0DM4S4jFUMdQy$ZtIbh86vL0nNCwfT< zEH)3j-#KWDx4v-jCA%GtDkjDtyAX$$5ZitgM!Cuh`vzBg&6fGGv%5l!&zEiidZC%}6J^Od> z?-(8)?r~$C7gTrQYhD{SZk&J3$_-z-^+@IBf+rFZJKdArptd|8;@>ZVyta}Mwv+Q< zw8SF5tN$Aj`+NJKj2Q0BDREj(#QrS->prGMgxvcxPOmThY}A**J@tpMJ=}`n*eFjJ zNQC4iMdl?qtV+Y{^`Iyx51aev;QHe=@M#J^c2+B<&vI)6e<$yqC&`DY(`V%V*6K~v zI-BfRpsR}@d~z<76=m*BZ2I{iZLsgQK>7)}m~oS14;GpnyDn&NYaBjs_ns#|`ph#= zw%|O^=6I|7gM}qamXzJJa^n}TOU!;p=|dxmly=%xvMb1Eu{!EYD`?RrCzs4Za=Qj# zeq}e*2gxMIFl`pqtNU}O{hLg%wjd1r(_#p>*774`h3+^485Jd&02-3HSR5(C(r6_X zCCV{BR*nVHQY?&?VKIHbVx$^N`%6)%`(gQQXqV=}W?0~TjS$$>fhRg4MYY$>3{C$u zBGE-wbZ74Eg4K`*ft|p{f;}usH!mNWmnh|)L9$39PwsoVGD2y2Md?VTJ?lj0sr|`h za`GEqo^)7M6M{4ei%2M1R{h1|skp62eltslZHzpaS4d`1sV1Hs4ynB+g zP~z_x-!QE&mzD0S#HYVJAD)eMM4`M8n(~nG6TiN?0^vXqb?JP}9W5jQmt#q~8q3m^ zSe7E8CM&TZS&q6;HGB&SVZOHknN?&}Zf}5hLoFZ8#ESS?c1d_~fS*SC+Z*k-+cj9c zSRN#;eXfT#8&RMpdQEaaOvtR5omGY4@HiiBD6^SgXEQ?mqK2w?oww-liNkvd97f+3 z0>13ZD=vH2yp5k*y3G?*dz=C}t;P?0X)%=5WiH4e%}ra#Jj|xH9)h<`cSixf5&Ix= zj;JjNLjK}%XO=WeG9Dk}L&d$flRsQv0efvZjGiczlSwyGKaJFrY0utTMt{Eq%En3v z)j7}(55hCMh^Om#(4P%)+0ze+ov`u-YI6j)Bu7e;xK9j1&m(uxT+Dm3_!NT~AsC}+ zNGIbQcrk5O2Q#97RYfpcrtUg&xVzT?m*F^z$-BxjWbGUp}w*J z>UA}IM{}gf%52$4-Y*@1(yn1r0ux8F3wZ7E4Q?@r!RiVBVKL+trA~8MOh@*YXP_=C zsBpDo2vM{5HaY5{@7Wrg;IUkd3vq~rk6I;u0bxe zbCV&{h5}7^48fkf4~Zxq2uU*<$T}n0;g^g~2@4Bh93!jH=2W-H#FxWfPcS~y@9&BZ z>>vu7ypRPf+;PWyuDN`y_Rp)fB>hUpp%nVfRZu^*khgkFjPldS=cYdM99nu$;;__> zZgCI+!Gn`hKlahT+oe7f!0Qb3p1!C|HVSFq<}xUM*5n)&KtjiPh&VX(`L^t)p*<83z=RRp)Zvysr zjv(4OhW{sQ#a^Qul|l&rS6Pp{ja@io4Pvoaj=6Xb2i*uZz z-JKEGcb|mJIw9ByL)$H0YZ-wQnO++{^*tY879|0-M1boYi^0Yk3g2si^>in!ch7dt ziy$qQ|9kQ|^m*^^-t4_$g}Af`Un1HG7k=*^zqYboTn&!3yDU z1XjeUEoLXE*((YllFv2&;|Ndc^C%dBB;}4q=Uc3)cKIfZ#$^tup zFnrE^($9wNq+@?r@MKHZlpUc>BP}a}Jv+yZiqU89%c5pHr)b5B6^kxd0hg7O&Mk<0 zee677zI;U8(0kdH@T&J02#jS$^d8a6pV?9BX5 z!%*4$OZnr?FlOdBKXM1l7!z|frlIV~VJq%`*t|~hOdPg8?O6E&vIezYCyv7k-m@b7 zorXGZA(^`ibfmwOBYEfgqwa`$)~J^2g;^xl@H8H>oq>uf4=}Cfg-*G*fg7;oP_6zG1f2zVQB%u5I*lAKjhLmk&ZaX=_uq# z_ID3bvn4*1+&nPk%(rCW(&`YbQWEAUZN{%3fN|D|j`1nE^YUT;^K$4+$db)JhTpA7 z=bw@E)qpu^mwbdBMECQvIy7P0$KZKkdLLHy9UpfJdfaVSSk&r}MIX8i&kIPTN9)7fZz% z@RDC)tu^MnqA5sgJM6-C`=2J=g<%UGk4L#+QMADNMMPn0#xt9sPG;0{N40RIN~X?O zMw+mH6kbcgJd_YxHBl@Eaj88I!%_xEgf4zOO&bld1U8OG?3P+yt>h>B2D~EGkmnTY?KoBtwg_!&+mfg$svA*A=g?~x?AX; zizqk;Go$^D45D#^5?yhQ4ftpz7L2I&@j1>CZ4`CTLgw%Gz^DmBURBBg7vtXE|1%%i znYozpr69g^yn)wH3+@;x7Az5Mr%zZfmtcLi37%`KC(o1OZMabl!kHww8s`TlF^*J~ zYMwoF_76%AzNH{5l8T;6ge{Z#Coaq}osR~BF_nv9yI5v}!T)vTzGLb-h^aKVbww4a}V`cxmU1#{vwbPIV3KWJ*=O-#}ZY21F*p;-3o z#I{Kw2?N4-+DRI)qQOEB_n8x~;29QSeX|ATp;2IBG$K9Ev%U)AH&#Nwxef^X=sw8n zS{_K2>YP%_=3;$f7UY{|LG;K|6pqn!av{R$qAJ)xP zkgu(W%!=0(C%0;*H0W%Y_IFP}PEJkJ<5roSS~ekI7UPa)$-JiBR3rG&IT<01G8-u6NpQU8YC#^9O||eWAy61l&h`B4%!XgJLOz>t>*qumbz!u7;~kxC zZO@!f4L+{|?&#~@-&P;lKexVYL(Z5(h2&lh+MO*BzOo8JSmkJxe|mO!GVxZiX(g;r zk|}2cOgti|I&z^iFkEoXVuuwc&As;oq;DL8Ix1mm=7kH>*gzD+@?2bFE=I8#;%3r_>a23wG1&p4cZ`YIQ|AJ+<7Jg? zL(o6}GNgYbAvfmodtjwHLJ67n&2_XXYhaRlU`Ofj>JC|$UdHeIm(^~)G64BkUHsHh zwizssiP4ol6@b`48P4s|46J0;^7|XcOU2N9h0Y` zxU@-PC8(7-P*xN{WueAmFGs8H{2J)}acHbs@vBF;aQUQkg?=7#P8R2#jR7+4iZEFf zB4}(pqKJf;#W(Dy@HPDfJZ-e0Ud-o%`5)Gf;_tLAxJTTB6dQlTIITXdTapV$niPv3 z7I~`;s{vD=9ESP%SD?Mz2U{~I54q;%oj{<%>a%hbK44U|&*jDJJy?7k-#AG3GEqIP zAkz@QzTBRUmk#ZH;(RB6&i9!O`j#C}zI3c1eI~Uihwn~ikEJ0C%D=Rbi9HO1p=x_- zQ8*Zz)~Yq}l@E0}8-@1uJ&?Y049cKmGTB*w$<==7>r2Q_x`71AbP=k%ly0TPxR}Nv ztCt5O*o|H)%|?@0?%)MGg%j<2zA{f+md~`12TA+BauoWR48+^Xzp`_9Ss$z{ zOm@@p0yzRaR%Z<6jsf7V)9{~+JIMxGEF(ukbdievw@B%w#{{2eVyJ;FVq%-{<7>lS zE_~K^qzTSw2B)r3oNO;))Xn6bjW%xtQ^@bKUr)41@tCtKVnfxjV`C=U?D5MLTQib8 zZo;=O0{vspLua$DZ=M00+>KD+d1Koo*Cx#&YIePw1pqvL1Qy z(U(q^)cD?e)6%7d4-P73{ zx2Be=bF3<>O=5-QVw65eg8t3gSWJ7eS^Zn};j-FA!5KH?AIm6WM(0TlVPiF}$MMzL_l{5>Vl&(Y>3Sqo|29&ES`H7}%qj1ypUbNpD(C^7T^qWUsdh)Ydp5C$xZ&5|%TdBePI(_=|2wgwPe`m<(URm|w z6$`89mOh_SfWB1{%*Pbu>z0L8a^^%&AI4aD(i>5J$p_j`tJVC_AO72jC45^ zLXt+~il)Kq@!1jLF9T#RBZSavf=+OPO>JFY0rSx=+}OGTO;R<6?KF}|(+yh)k^a`B z1Qli}j3P3#n`+$i3t62Qp=$D(Z14#OYlMcgxsuiXLN}X-VGR!t5ANQ!``-%+3U-~X zsJwl_ZROWDjujRM_oX1W#rZr+(Q+bMJTA@$w~DZ80~lH3H(GM~JN6_FKJobTTkkw{ z=upS`KBFa=X9a#w%`J^`0iHqOPP9!JPKEC`1K8G}$>q zx@{GB>_nZ{=&97k4yQVf9_c#ui~jV;GY>!fFq=m))h?-`Z^)Y(G6KG^!a{>RX_zO* zoddGqj5|_SRzUlk1@L}pKbG|Qo%16E!5vNHocyAdkU9NyGt}k+PR;lM(Cm2k!%+@| zIKax0(d~_8J3G%#l+xI(W3938{os4g&RVl(&x)EEcNKYpo95j(``SjiytXLm@%bXU z!UmMG(`4d7%Nz{rV{O^K?x*%YyDNh9&q=VS1_uX|_#<30{y3MuWy=;;G5Eb#7VO=6 z=_TuyHqD%|sC-Vi+G{ESza-jONr)%JWTG?G-f^hy#J=5o_qLopdbIbo%vaYyS8Skk zW@j=0R)H+Af;eqkFRUBuc+#E&C0bnd9SwY9j`jd43L6v}ci|TKKy!8;?3?SLZm!{B z$H{c=@hHSo2`9)w8-eu;bz8$d@p$~zr-It2UV14`k3IDLp~l+UdlxQWHM_2+x}u;u zuhir52=R1M7#tlQ8#vi_c-M=&+Pb^DS?zNQ=jq6t3rWC}35gReCyucPeSaT4LS5K82{8hP!jT|<~R@&ANWA_lm%gWrr+cwV( zsqdW)V{H}eE<&^s)5#?V*+E=kNRmv2w!FfhYT71Muu z8fH^2&n!&@99S${t|Q+}K2SJ!FWeTB-+!nTG9h8n1-(YTg0AXp{OoY|_BVNN ze^Q~om65DJ)@AS9xij;4&XZoH_WEp@Tiho=ke<4dXwFPL}RqA-M}{wK{*_ErpS_EGrcCxZuTYb@7?k9 z`|iK***~V=yeMGjN_BO19;;q6r?IBEsxi=N%AyX3d+r&8c3{K_RIo7;lFL&khQ4GC zSR+}O2gjiQ;25NTIs~;f%UebH$R?kS@vHLjV8yYge*9lQ{8q*=#@?=rB;)OUWp|z2 z^^Fgx80kiHS@|?5sd>+W^d6qcEC%u!&RKrD1?|R%iyyF#=qtVh~%B@D6G` z%*l_n;fV$tu{A+#YwjPt^X?x%KAy>RznvH8?Ji*a`qx`ds0jSPT)wVugHqAp-!{%m zc_dSV7&RcjOq6F&jKG!*i?xne79>M=^6b{cY%hlFIj* z?Y=$|-u{<;?BuaCJ=(~Np3BN>^X8ORdgEzXVwA|u9`P}NBv6sXxOEl|72RXxoU|f4 z5j0zEqW|Vx?Wu*sFaG$sUw;14Cmw&4ejU9i;0r{^Jv}`GR*rNiQ7KxP8NR}t@`79? z$0OJo%^B!vTT?Bdtjk_BVX#DgV-ET*_1i6r)4ea{cmMpJtq**2$IhJx-makP)t7+( zWmhg;8#Zj1yRc%;=4wyj)%8+oV|hGB_7Cfl8gmYa&ITnUi$rTAPs^Oq28Q+xp5AtL zsB3G>u>;S&*~1?%3iwZmFFYPk&Af#Rm#tjAGQT)nkZZ%rwM<)(BqiEA(icB<^myz3 z{YP5k@zK*4QgGtpb@94*UA!({7q5%g#p~jA@w#|jye?iBuZ!3J?E3!zL>G3fRtO$< P00000NkvXXu0mjfrG)<- literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-152.png b/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-152.png new file mode 100755 index 0000000000000000000000000000000000000000..d799a58935b2992ac3f72a2a7813b4e15bf0c664 GIT binary patch literal 30725 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 IM6N<$f^&#+aR2}S literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-167.png b/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-167.png new file mode 100755 index 0000000000000000000000000000000000000000..c5d19fd5a68f9069cfe3d5c391b7c4ee9c827dbd GIT binary patch literal 35774 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 T)B6Pz00000NkvXXu0mjfNMKP< literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-58.png b/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-58.png new file mode 100755 index 0000000000000000000000000000000000000000..ff8e5f74285dd29eb2f483ffcd9f2d51a942c8d7 GIT binary patch literal 6207 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 literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-76.png b/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-76.png new file mode 100755 index 0000000000000000000000000000000000000000..9ee397dee034ed3dd1c38923448f35db1705de1b GIT binary patch literal 9838 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 UP*AMT;{X5v07*qoM6N<$f@dAuj{pDw literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-80.png b/App/Assets.xcassets/AppIcon.appiconset/ic_launcher-80.png new file mode 100755 index 0000000000000000000000000000000000000000..83841f50a0332210b6f861364165c45152ed46a8 GIT binary patch literal 10920 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 KMNUMnLSTY+M##

EN?slW0)fz9 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

+ +Kingfisher + +

+ +

+ + + + + + + + + + + + + +codebeat badge + + + + +

+ +Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app. + +## Features + +- [x] Asynchronous image downloading and caching. +- [x] `URLSession`-based networking. Basic image processors and filters supplied. +- [x] Multiple-layer cache for both memory and disk. +- [x] Cancelable downloading and processing tasks to improve performance. +- [x] Independent components. Use the downloader or caching system separately as you need. +- [x] Prefetching images and showing them from cache later when necessary. +- [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL. +- [x] Built-in transition animation when setting images. +- [x] Extensible image processing and image format support. + +The simplest use-case is setting an image to an image view with the `UIImageView` extension: + +```swift +let url = URL(string: "url_of_your_image") +imageView.kf.setImage(with: url) +``` + +Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately. + +## Requirements + +- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ +- Swift 3 (Kingfisher 3.x), Swift 2.3 (Kingfisher 2.x) + +Main development of Kingfisher will support Swift 3. Only critical bug fixes will be made for Kingfisher 2.x. + +[Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information. + +## Next Steps + +We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there. + +* [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project. +* [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! +* [API Reference](http://cocoadocs.org/docsets/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference. + +## Other + +### Future of Kingfisher + +I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better. + +### Developments and Tests + +Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. + +The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :) + +### About the logo + +The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions? + +### Contact + +Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well. + +### License + +Kingfisher is released under the MIT license. See LICENSE for details. + + diff --git a/Pods/Kingfisher/Sources/AnimatedImageView.swift b/Pods/Kingfisher/Sources/AnimatedImageView.swift new file mode 100755 index 0000000..5ea04dc --- /dev/null +++ b/Pods/Kingfisher/Sources/AnimatedImageView.swift @@ -0,0 +1,355 @@ +// +// AnimatableImageView.swift +// Kingfisher +// +// Created by bl4ckra1sond3tre on 4/22/16. +// +// The AnimatableImageView, AnimatedFrame and Animator is a modified version of +// some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu) +// +// The MIT License (MIT) +// +// Copyright (c) 2017 Reda Lemeden. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// The name and characters used in the demo of this software are property of their +// respective owners. + +import UIKit +import ImageIO + +/// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image. +open class AnimatedImageView: UIImageView { + + /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView. + class TargetProxy { + private weak var target: AnimatedImageView? + + init(target: AnimatedImageView) { + self.target = target + } + + @objc func onScreenUpdate() { + target?.updateFrame() + } + } + + // MARK: - Public property + /// Whether automatically play the animation when the view become visible. Default is true. + public var autoPlayAnimatedImage = true + + /// The size of the frame cache. + public var framePreloadCount = 10 + + /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true. + public var needsPrescaling = true + + /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling. + public var runLoopMode = RunLoopMode.commonModes { + willSet { + if runLoopMode == newValue { + return + } else { + stopAnimating() + displayLink.remove(from: .main, forMode: runLoopMode) + displayLink.add(to: .main, forMode: newValue) + startAnimating() + } + } + } + + // MARK: - Private property + /// `Animator` instance that holds the frames of a specific image in memory. + private var animator: Animator? + + /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D + private var isDisplayLinkInitialized: Bool = false + + /// A display link that keeps calling the `updateFrame` method on every screen refresh. + private lazy var displayLink: CADisplayLink = { + self.isDisplayLinkInitialized = true + let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate)) + displayLink.add(to: .main, forMode: self.runLoopMode) + displayLink.isPaused = true + return displayLink + }() + + // MARK: - Override + override open var image: Image? { + didSet { + if image != oldValue { + reset() + } + setNeedsDisplay() + layer.setNeedsDisplay() + } + } + + deinit { + if isDisplayLinkInitialized { + displayLink.invalidate() + } + } + + override open var isAnimating: Bool { + if isDisplayLinkInitialized { + return !displayLink.isPaused + } else { + return super.isAnimating + } + } + + /// Starts the animation. + override open func startAnimating() { + if self.isAnimating { + return + } else { + displayLink.isPaused = false + } + } + + /// Stops the animation. + override open func stopAnimating() { + super.stopAnimating() + if isDisplayLinkInitialized { + displayLink.isPaused = true + } + } + + override open func display(_ layer: CALayer) { + if let currentFrame = animator?.currentFrame { + layer.contents = currentFrame.cgImage + } else { + layer.contents = image?.cgImage + } + } + + override open func didMoveToWindow() { + super.didMoveToWindow() + didMove() + } + + override open func didMoveToSuperview() { + super.didMoveToSuperview() + didMove() + } + + // This is for back compatibility that using regular UIImageView to show GIF. + override func shouldPreloadAllGIF() -> Bool { + return false + } + + // MARK: - Private method + /// Reset the animator. + private func reset() { + animator = nil + if let imageSource = image?.kf.imageSource?.imageRef { + animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount) + animator?.needsPrescaling = needsPrescaling + animator?.prepareFramesAsynchronously() + } + didMove() + } + + private func didMove() { + if autoPlayAnimatedImage && animator != nil { + if let _ = superview, let _ = window { + startAnimating() + } else { + stopAnimating() + } + } + } + + /// Update the current frame with the displayLink duration. + private func updateFrame() { + if animator?.updateCurrentFrame(duration: displayLink.duration) ?? false { + layer.setNeedsDisplay() + } + } +} + +/// Keeps a reference to an `Image` instance and its duration as a GIF frame. +struct AnimatedFrame { + var image: Image? + let duration: TimeInterval + + static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0) +} + +// MARK: - Animator +class Animator { + // MARK: Private property + fileprivate let size: CGSize + fileprivate let maxFrameCount: Int + fileprivate let imageSource: CGImageSource + + fileprivate var animatedFrames = [AnimatedFrame]() + fileprivate let maxTimeStep: TimeInterval = 1.0 + fileprivate var frameCount = 0 + fileprivate var currentFrameIndex = 0 + fileprivate var currentPreloadIndex = 0 + fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0 + fileprivate var needsPrescaling = true + + /// Loop count of animatd image. + private var loopCount = 0 + + var currentFrame: UIImage? { + return frame(at: currentFrameIndex) + } + + var contentMode = UIViewContentMode.scaleToFill + + private lazy var preloadQueue: DispatchQueue = { + return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue") + }() + + /** + Init an animator with image source reference. + + - parameter imageSource: The reference of animated image. + - parameter contentMode: Content mode of AnimatedImageView. + - parameter size: Size of AnimatedImageView. + - parameter framePreloadCount: Frame cache size. + + - returns: The animator object. + */ + init(imageSource source: CGImageSource, contentMode mode: UIViewContentMode, size: CGSize, framePreloadCount count: Int) { + self.imageSource = source + self.contentMode = mode + self.size = size + self.maxFrameCount = count + } + + func frame(at index: Int) -> Image? { + return animatedFrames[safe: index]?.image + } + + func prepareFramesAsynchronously() { + preloadQueue.async { [weak self] in + self?.prepareFrames() + } + } + + private func prepareFrames() { + frameCount = CGImageSourceGetCount(imageSource) + + if let properties = CGImageSourceCopyProperties(imageSource, nil), + let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary, + let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int + { + self.loopCount = loopCount + } + + let frameToProcess = min(frameCount, maxFrameCount) + animatedFrames.reserveCapacity(frameToProcess) + animatedFrames = (0.. AnimatedFrame { + + guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else { + return AnimatedFrame.null + } + + let defaultGIFFrameDuration = 0.100 + let frameDuration = imageSource.kf.gifProperties(at: index).map { + gifInfo -> Double in + + let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double? + let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double? + let duration = unclampedDelayTime ?? delayTime ?? 0.0 + + /** + http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp + Many annoying ads specify a 0 duration to make an image flash as quickly as + possible. We follow Safari and Firefox's behavior and use a duration of 100 ms + for any frames that specify a duration of <= 10 ms. + See and for more information. + + See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser. + */ + return duration > 0.011 ? duration : defaultGIFFrameDuration + } ?? defaultGIFFrameDuration + + let image = Image(cgImage: imageRef) + let scaledImage: Image? + + if needsPrescaling { + scaledImage = image.kf.resize(to: size, for: contentMode) + } else { + scaledImage = image + } + + return AnimatedFrame(image: scaledImage, duration: frameDuration) + } + + /** + Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`. + */ + func updateCurrentFrame(duration: CFTimeInterval) -> Bool { + timeSinceLastFrameChange += min(maxTimeStep, duration) + guard let frameDuration = animatedFrames[safe: currentFrameIndex]?.duration, frameDuration <= timeSinceLastFrameChange else { + return false + } + + timeSinceLastFrameChange -= frameDuration + + let lastFrameIndex = currentFrameIndex + currentFrameIndex += 1 + currentFrameIndex = currentFrameIndex % animatedFrames.count + + if animatedFrames.count < frameCount { + preloadFrameAsynchronously(at: lastFrameIndex) + } + return true + } + + private func preloadFrameAsynchronously(at index: Int) { + preloadQueue.async { [weak self] in + self?.preloadFrame(at: index) + } + } + + private func preloadFrame(at index: Int) { + animatedFrames[index] = prepareFrame(at: currentPreloadIndex) + currentPreloadIndex += 1 + currentPreloadIndex = currentPreloadIndex % frameCount + } +} + +extension CGImageSource: KingfisherCompatible { } +extension Kingfisher where Base: CGImageSource { + func gifProperties(at index: Int) -> [String: Double]? { + let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary? + return properties?[kCGImagePropertyGIFDictionary] as? [String: Double] + } +} + +extension Array { + subscript(safe index: Int) -> Element? { + return indices ~= index ? self[index] : nil + } +} + +private func pure(_ value: T) -> [T] { + return [value] +} diff --git a/Pods/Kingfisher/Sources/Box.swift b/Pods/Kingfisher/Sources/Box.swift new file mode 100644 index 0000000..7076add --- /dev/null +++ b/Pods/Kingfisher/Sources/Box.swift @@ -0,0 +1,16 @@ +// +// Box.swift +// Kingfisher +// +// Created by WANG WEI on 2016/09/12. +// Copyright © 2016年 Wei Wang. All rights reserved. +// + +import Foundation + +class Box { + let value: T + init(value: T) { + self.value = value + } +} diff --git a/Pods/Kingfisher/Sources/CacheSerializer.swift b/Pods/Kingfisher/Sources/CacheSerializer.swift new file mode 100644 index 0000000..8d8cdc9 --- /dev/null +++ b/Pods/Kingfisher/Sources/CacheSerializer.swift @@ -0,0 +1,87 @@ +// +// CacheSerializer.swift +// Kingfisher +// +// Created by Wei Wang on 2016/09/02. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +/// An `CacheSerializer` would be used to convert some data to an image object for +/// retrieving from disk cache and vice versa for storing to disk cache. +public protocol CacheSerializer { + + /// Get the serialized data from a provided image + /// and optional original data for caching to disk. + /// + /// + /// - parameter image: The image needed to be serialized. + /// - parameter original: The original data which is just downloaded. + /// If the image is retrieved from cache instead of + /// downloaded, it will be `nil`. + /// + /// - returns: A data which will be stored to cache, or `nil` when no valid + /// data could be serialized. + func data(with image: Image, original: Data?) -> Data? + + /// Get an image deserialized from provided data. + /// + /// - parameter data: The data from which an image should be deserialized. + /// - parameter options: Options for deserialization. + /// + /// - returns: An image deserialized or `nil` when no valid image + /// could be deserialized. + func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? +} + + +/// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of +/// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For +/// image other than these formats, a normalized `pngRepresentation` will be used. +public struct DefaultCacheSerializer: CacheSerializer { + + public static let `default` = DefaultCacheSerializer() + private init() {} + + public func data(with image: Image, original: Data?) -> Data? { + let imageFormat = original?.kf.imageFormat ?? .unknown + + let data: Data? + switch imageFormat { + case .PNG: data = image.kf.pngRepresentation() + case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) + case .GIF: data = image.kf.gifRepresentation() + case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() + } + + return data + } + + public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { + let options = options ?? KingfisherEmptyOptionsInfo + return Kingfisher.image( + data: data, + scale: options.scaleFactor, + preloadAllGIFData: options.preloadAllGIFData, + onlyFirstFrame: options.onlyLoadFirstFrame) + } +} diff --git a/Pods/Kingfisher/Sources/Filter.swift b/Pods/Kingfisher/Sources/Filter.swift new file mode 100644 index 0000000..5f20987 --- /dev/null +++ b/Pods/Kingfisher/Sources/Filter.swift @@ -0,0 +1,145 @@ +// +// Filter.swift +// Kingfisher +// +// Created by Wei Wang on 2016/08/31. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +import CoreImage +import Accelerate + +// Reuse the same CI Context for all CI drawing. +private let ciContext = CIContext(options: nil) + +/// Transformer method which will be used in to provide a `Filter`. +public typealias Transformer = (CIImage) -> CIImage? + +/// Supply a filter to create an `ImageProcessor`. +public protocol CIImageProcessor: ImageProcessor { + var filter: Filter { get } +} + +extension CIImageProcessor { + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf.apply(filter) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + +/// Wrapper for a `Transformer` of CIImage filters. +public struct Filter { + + let transform: Transformer + + public init(tranform: @escaping Transformer) { + self.transform = tranform + } + + /// Tint filter which will apply a tint color to images. + public static var tint: (Color) -> Filter = { + color in + Filter { input in + let colorFilter = CIFilter(name: "CIConstantColorGenerator")! + colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) + + let colorImage = colorFilter.outputImage + let filter = CIFilter(name: "CISourceOverCompositing")! + filter.setValue(colorImage, forKey: kCIInputImageKey) + filter.setValue(input, forKey: kCIInputBackgroundImageKey) + return filter.outputImage?.cropping(to: input.extent) + } + } + + public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) + + /// Color control filter which will apply color control change to images. + public static var colorControl: (ColorElement) -> Filter = { + brightness, contrast, saturation, inputEV in + Filter { input in + let paramsColor = [kCIInputBrightnessKey: brightness, + kCIInputContrastKey: contrast, + kCIInputSaturationKey: saturation] + + let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) + let paramsExposure = [kCIInputEVKey: inputEV] + return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) + } + + } +} + +extension Kingfisher where Base: Image { + /// Apply a `Filter` containing `CIImage` transformer to `self`. + /// + /// - parameter filter: The filter used to transform `self`. + /// + /// - returns: A transformed image by input `Filter`. + /// + /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. + public func apply(_ filter: Filter) -> Image { + + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Tint image only works for CG-based image.") + return base + } + + let inputImage = CIImage(cgImage: cgImage) + guard let outputImage = filter.transform(inputImage) else { + return base + } + + guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { + assertionFailure("[Kingfisher] Can not make an tint image within context.") + return base + } + + #if os(macOS) + return fixedForRetinaPixel(cgImage: result, to: size) + #else + return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) + #endif + } + +} + +public extension Image { + + /// Apply a `Filter` containing `CIImage` transformer to `self`. + /// + /// - parameter filter: The filter used to transform `self`. + /// + /// - returns: A transformed image by input `Filter`. + /// + /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.apply` instead.", + renamed: "kf.apply") + public func kf_apply(_ filter: Filter) -> Image { + return kf.apply(filter) + } +} diff --git a/Pods/Kingfisher/Sources/Image.swift b/Pods/Kingfisher/Sources/Image.swift new file mode 100755 index 0000000..23b2889 --- /dev/null +++ b/Pods/Kingfisher/Sources/Image.swift @@ -0,0 +1,935 @@ +// +// Image.swift +// Kingfisher +// +// Created by Wei Wang on 16/1/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#if os(macOS) +import AppKit +private var imagesKey: Void? +private var durationKey: Void? +#else +import UIKit +import MobileCoreServices +private var imageSourceKey: Void? +#endif +private var animatedImageDataKey: Void? + +import ImageIO +import CoreGraphics + +#if !os(watchOS) +import Accelerate +import CoreImage +#endif + +// MARK: - Image Properties +extension Kingfisher where Base: Image { + fileprivate(set) var animatedImageData: Data? { + get { + return objc_getAssociatedObject(base, &animatedImageDataKey) as? Data + } + set { + objc_setAssociatedObject(base, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + #if os(macOS) + var cgImage: CGImage? { + return base.cgImage(forProposedRect: nil, context: nil, hints: nil) + } + + var scale: CGFloat { + return 1.0 + } + + fileprivate(set) var images: [Image]? { + get { + return objc_getAssociatedObject(base, &imagesKey) as? [Image] + } + set { + objc_setAssociatedObject(base, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + fileprivate(set) var duration: TimeInterval { + get { + return objc_getAssociatedObject(base, &durationKey) as? TimeInterval ?? 0.0 + } + set { + objc_setAssociatedObject(base, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + var size: CGSize { + return base.representations.reduce(CGSize.zero, { size, rep in + return CGSize(width: max(size.width, CGFloat(rep.pixelsWide)), height: max(size.height, CGFloat(rep.pixelsHigh))) + }) + } + + #else + var cgImage: CGImage? { + return base.cgImage + } + + var scale: CGFloat { + return base.scale + } + + var images: [Image]? { + return base.images + } + + var duration: TimeInterval { + return base.duration + } + + fileprivate(set) var imageSource: ImageSource? { + get { + return objc_getAssociatedObject(base, &imageSourceKey) as? ImageSource + } + set { + objc_setAssociatedObject(base, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + var size: CGSize { + return base.size + } + #endif +} + +// MARK: - Image Conversion +extension Kingfisher where Base: Image { + #if os(macOS) + static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { + return Image(cgImage: cgImage, size: CGSize.zero) + } + + /** + Normalize the image. This method does nothing in OS X. + + - returns: The image itself. + */ + public var normalized: Image { + return base + } + + static func animated(with images: [Image], forDuration forDurationduration: TimeInterval) -> Image? { + return nil + } + #else + static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image { + if let refImage = refImage { + return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation) + } else { + return Image(cgImage: cgImage, scale: scale, orientation: .up) + } + } + + /** + Normalize the image. This method will try to redraw an image with orientation and scale considered. + + - returns: The normalized image with orientation set to up and correct scale. + */ + public var normalized: Image { + // prevent animated image (GIF) lose it's images + guard images == nil else { return base } + // No need to do anything if already up + guard base.imageOrientation != .up else { return base } + + return draw(cgImage: nil, to: size) { + base.draw(in: CGRect(origin: CGPoint.zero, size: size)) + } + } + + static func animated(with images: [Image], forDuration duration: TimeInterval) -> Image? { + return .animatedImage(with: images, duration: duration) + } + #endif +} + +// MARK: - Image Representation +extension Kingfisher where Base: Image { + // MARK: - PNG + public func pngRepresentation() -> Data? { + #if os(macOS) + guard let cgimage = cgImage else { + return nil + } + let rep = NSBitmapImageRep(cgImage: cgimage) + return rep.representation(using: .PNG, properties: [:]) + #else + return UIImagePNGRepresentation(base) + #endif + } + + // MARK: - JPEG + public func jpegRepresentation(compressionQuality: CGFloat) -> Data? { + #if os(macOS) + guard let cgImage = cgImage else { + return nil + } + let rep = NSBitmapImageRep(cgImage: cgImage) + return rep.representation(using:.JPEG, properties: [NSImageCompressionFactor: compressionQuality]) + #else + return UIImageJPEGRepresentation(base, compressionQuality) + #endif + } + + // MARK: - GIF + public func gifRepresentation() -> Data? { + return animatedImageData + } +} + +// MARK: - Create images from data +extension Kingfisher where Base: Image { + static func animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool, onlyFirstFrame: Bool = false) -> Image? { + + func decode(from imageSource: CGImageSource, for options: NSDictionary) -> ([Image], TimeInterval)? { + + //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary + func frameDuration(from gifInfo: NSDictionary?) -> Double { + let gifDefaultFrameDuration = 0.100 + + guard let gifInfo = gifInfo else { + return gifDefaultFrameDuration + } + + let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber + let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber + let duration = unclampedDelayTime ?? delayTime + + guard let frameDuration = duration else { return gifDefaultFrameDuration } + + return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : gifDefaultFrameDuration + } + + let frameCount = CGImageSourceGetCount(imageSource) + var images = [Image]() + var gifDuration = 0.0 + for i in 0 ..< frameCount { + + guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else { + return nil + } + + if frameCount == 1 { + // Single frame + gifDuration = Double.infinity + } else { + + // Animated GIF + guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) else { + return nil + } + + let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary + gifDuration += frameDuration(from: gifInfo) + } + + images.append(Kingfisher.image(cgImage: imageRef, scale: scale, refImage: nil)) + + if onlyFirstFrame { break } + } + + return (images, gifDuration) + } + + // Start of kf.animatedImageWithGIFData + let options: NSDictionary = [kCGImageSourceShouldCache as String: true, kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF] + guard let imageSource = CGImageSourceCreateWithData(data as CFData, options) else { + return nil + } + + #if os(macOS) + guard let (images, gifDuration) = decode(from: imageSource, for: options) else { + return nil + } + let image: Image? + if onlyFirstFrame { + image = images.first + } else { + image = Image(data: data) + image?.kf.images = images + image?.kf.duration = gifDuration + } + image?.kf.animatedImageData = data + return image + #else + + let image: Image? + if preloadAll || onlyFirstFrame { + guard let (images, gifDuration) = decode(from: imageSource, for: options) else { return nil } + image = onlyFirstFrame ? images.first : Kingfisher.animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration) + } else { + image = Image(data: data) + image?.kf.imageSource = ImageSource(ref: imageSource) + } + image?.kf.animatedImageData = data + return image + #endif + } + + static func image(data: Data, scale: CGFloat, preloadAllGIFData: Bool, onlyFirstFrame: Bool) -> Image? { + var image: Image? + + #if os(macOS) + switch data.kf.imageFormat { + case .JPEG: + image = Image(data: data) + case .PNG: + image = Image(data: data) + case .GIF: + image = Kingfisher.animated( + with: data, + scale: scale, + duration: 0.0, + preloadAll: preloadAllGIFData, + onlyFirstFrame: onlyFirstFrame) + case .unknown: + image = Image(data: data) + } + #else + switch data.kf.imageFormat { + case .JPEG: + image = Image(data: data, scale: scale) + case .PNG: + image = Image(data: data, scale: scale) + case .GIF: + image = Kingfisher.animated( + with: data, + scale: scale, + duration: 0.0, + preloadAll: preloadAllGIFData, + onlyFirstFrame: onlyFirstFrame) + case .unknown: + image = Image(data: data, scale: scale) + } + #endif + + return image + } +} + +// MARK: - Image Transforming +extension Kingfisher where Base: Image { + + // MARK: - Round Corner + /// Create a round corner image based on `self`. + /// + /// - parameter radius: The round corner radius of creating image. + /// - parameter size: The target size of creating image. + /// + /// - returns: An image with round corner of `self`. + /// + /// - Note: This method only works for CG-based image. + public func image(withRoundRadius radius: CGFloat, fit size: CGSize) -> Image { + + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Round corner image only works for CG-based image.") + return base + } + + let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) + return draw(cgImage: cgImage, to: size) { + #if os(macOS) + let path = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius) + path.windingRule = .evenOddWindingRule + path.addClip() + base.draw(in: rect) + #else + guard let context = UIGraphicsGetCurrentContext() else { + assertionFailure("[Kingfisher] Failed to create CG context for image.") + return + } + let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)).cgPath + context.addPath(path) + context.clip() + base.draw(in: rect) + #endif + } + } + + #if os(iOS) || os(tvOS) + func resize(to size: CGSize, for contentMode: UIViewContentMode) -> Image { + switch contentMode { + case .scaleAspectFit: + return resize(to: size, for: .aspectFit) + case .scaleAspectFill: + return resize(to: size, for: .aspectFill) + default: + return resize(to: size) + } + } + #endif + + // MARK: - Resize + /// Resize `self` to an image of new size. + /// + /// - parameter size: The target size. + /// + /// - returns: An image with new size. + /// + /// - Note: This method only works for CG-based image. + public func resize(to size: CGSize) -> Image { + + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Resize only works for CG-based image.") + return base + } + + let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) + return draw(cgImage: cgImage, to: size) { + #if os(macOS) + base.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) + #else + base.draw(in: rect) + #endif + } + } + + /// Resize `self` to an image of new size, respecting the content mode. + /// + /// - Parameters: + /// - size: The target size. + /// - contentMode: Content mode of output image should be. + /// - Returns: An image with new size. + public func resize(to size: CGSize, for contentMode: ContentMode) -> Image { + switch contentMode { + case .aspectFit: + let newSize = self.size.kf.constrained(size) + return resize(to: newSize) + case .aspectFill: + let newSize = self.size.kf.filling(size) + return resize(to: newSize) + default: + return resize(to: size) + } + } + + // MARK: - Blur + + /// Create an image with blur effect based on `self`. + /// + /// - parameter radius: The blur radius should be used when creating blue. + /// + /// - returns: An image with blur effect applied. + /// + /// - Note: This method only works for CG-based image. + public func blurred(withRadius radius: CGFloat) -> Image { + #if os(watchOS) + return base + #else + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Blur only works for CG-based image.") + return base + } + + // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement + // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) + // if d is odd, use three box-blurs of size 'd', centered on the output pixel. + let s = Float(max(radius, 2.0)) + // We will do blur on a resized image (*0.5), so the blur radius could be half as well. + var targetRadius = floor(s * 3.0 * sqrt(2 * Float.pi) / 4.0 + 0.5) + + if targetRadius.isEven { + targetRadius += 1 + } + + let iterations: Int + if radius < 0.5 { + iterations = 1 + } else if radius < 1.5 { + iterations = 2 + } else { + iterations = 3 + } + + let w = Int(size.width) + let h = Int(size.height) + let rowBytes = Int(CGFloat(cgImage.bytesPerRow)) + + func createEffectBuffer(_ context: CGContext) -> vImage_Buffer { + let data = context.data + let width = vImagePixelCount(context.width) + let height = vImagePixelCount(context.height) + let rowBytes = context.bytesPerRow + + return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes) + } + + guard let context = beginContext() else { + assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") + return base + } + defer { endContext() } + + context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h)) + + var inBuffer = createEffectBuffer(context) + + guard let outContext = beginContext() else { + assertionFailure("[Kingfisher] Failed to create CG context for blurring image.") + return base + } + defer { endContext() } + var outBuffer = createEffectBuffer(outContext) + + for _ in 0 ..< iterations { + vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, UInt32(targetRadius), UInt32(targetRadius), nil, vImage_Flags(kvImageEdgeExtend)) + (inBuffer, outBuffer) = (outBuffer, inBuffer) + } + + #if os(macOS) + let result = outContext.makeImage().flatMap { fixedForRetinaPixel(cgImage: $0, to: size) } + #else + let result = outContext.makeImage().flatMap { Image(cgImage: $0, scale: base.scale, orientation: base.imageOrientation) } + #endif + guard let blurredImage = result else { + assertionFailure("[Kingfisher] Can not make an blurred image within this context.") + return base + } + + return blurredImage + #endif + } + + // MARK: - Overlay + + /// Create an image from `self` with a color overlay layer. + /// + /// - parameter color: The color should be use to overlay. + /// - parameter fraction: Fraction of input color. From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. + /// + /// - returns: An image with a color overlay applied. + /// + /// - Note: This method only works for CG-based image. + public func overlaying(with color: Color, fraction: CGFloat) -> Image { + + guard let cgImage = cgImage else { + assertionFailure("[Kingfisher] Overlaying only works for CG-based image.") + return base + } + + let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) + return draw(cgImage: cgImage, to: rect.size) { + #if os(macOS) + base.draw(in: rect) + if fraction > 0 { + color.withAlphaComponent(1 - fraction).set() + NSRectFillUsingOperation(rect, .sourceAtop) + } + #else + color.set() + UIRectFill(rect) + base.draw(in: rect, blendMode: .destinationIn, alpha: 1.0) + + if fraction > 0 { + base.draw(in: rect, blendMode: .sourceAtop, alpha: fraction) + } + #endif + } + } + + // MARK: - Tint + + /// Create an image from `self` with a color tint. + /// + /// - parameter color: The color should be used to tint `self` + /// + /// - returns: An image with a color tint applied. + public func tinted(with color: Color) -> Image { + #if os(watchOS) + return base + #else + return apply(.tint(color)) + #endif + } + + // MARK: - Color Control + + /// Create an image from `self` with color control. + /// + /// - parameter brightness: Brightness changing to image. + /// - parameter contrast: Contrast changing to image. + /// - parameter saturation: Saturation changing to image. + /// - parameter inputEV: InputEV changing to image. + /// + /// - returns: An image with color control applied. + public func adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { + #if os(watchOS) + return base + #else + return apply(.colorControl(brightness, contrast, saturation, inputEV)) + #endif + } +} + +// MARK: - Decode +extension Kingfisher where Base: Image { + var decoded: Image? { + return decoded(scale: scale) + } + + func decoded(scale: CGFloat) -> Image { + // prevent animated image (GIF) lose it's images + #if os(iOS) + if imageSource != nil { return base } + #else + if images != nil { return base } + #endif + + guard let imageRef = self.cgImage else { + assertionFailure("[Kingfisher] Decoding only works for CG-based image.") + return base + } + let colorSpace = CGColorSpaceCreateDeviceRGB() + guard let context = beginContext() else { + assertionFailure("[Kingfisher] Decoding fails to create a valid context.") + return base + } + + defer { endContext() } + + let rect = CGRect(x: 0, y: 0, width: imageRef.width, height: imageRef.height) + context.draw(imageRef, in: rect) + let decompressedImageRef = context.makeImage() + return Kingfisher.image(cgImage: decompressedImageRef!, scale: scale, refImage: base) + } +} + +/// Reference the source image reference +class ImageSource { + var imageRef: CGImageSource? + init(ref: CGImageSource) { + self.imageRef = ref + } +} + +// MARK: - Image format +private struct ImageHeaderData { + static var PNG: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] + static var JPEG_SOI: [UInt8] = [0xFF, 0xD8] + static var JPEG_IF: [UInt8] = [0xFF] + static var GIF: [UInt8] = [0x47, 0x49, 0x46] +} + +enum ImageFormat { + case unknown, PNG, JPEG, GIF +} + + +// MARK: - Misc Helpers +public struct DataProxy { + fileprivate let base: Data + init(proxy: Data) { + base = proxy + } +} + +extension Data: KingfisherCompatible { + public typealias CompatibleType = DataProxy + public var kf: DataProxy { + return DataProxy(proxy: self) + } +} + +extension DataProxy { + var imageFormat: ImageFormat { + var buffer = [UInt8](repeating: 0, count: 8) + (base as NSData).getBytes(&buffer, length: 8) + if buffer == ImageHeaderData.PNG { + return .PNG + } else if buffer[0] == ImageHeaderData.JPEG_SOI[0] && + buffer[1] == ImageHeaderData.JPEG_SOI[1] && + buffer[2] == ImageHeaderData.JPEG_IF[0] + { + return .JPEG + } else if buffer[0] == ImageHeaderData.GIF[0] && + buffer[1] == ImageHeaderData.GIF[1] && + buffer[2] == ImageHeaderData.GIF[2] + { + return .GIF + } + + return .unknown + } +} + +public struct CGSizeProxy { + fileprivate let base: CGSize + init(proxy: CGSize) { + base = proxy + } +} + +extension CGSize: KingfisherCompatible { + public typealias CompatibleType = CGSizeProxy + public var kf: CGSizeProxy { + return CGSizeProxy(proxy: self) + } +} + +extension CGSizeProxy { + func constrained(_ size: CGSize) -> CGSize { + let aspectWidth = round(aspectRatio * size.height) + let aspectHeight = round(size.width / aspectRatio) + + return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) + } + + func filling(_ size: CGSize) -> CGSize { + let aspectWidth = round(aspectRatio * size.height) + let aspectHeight = round(size.width / aspectRatio) + + return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height) + } + + private var aspectRatio: CGFloat { + return base.height == 0.0 ? 1.0 : base.width / base.height + } +} + +extension Kingfisher where Base: Image { + + func beginContext() -> CGContext? { + #if os(macOS) + guard let rep = NSBitmapImageRep( + bitmapDataPlanes: nil, + pixelsWide: Int(size.width), + pixelsHigh: Int(size.height), + bitsPerSample: cgImage?.bitsPerComponent ?? 8, + samplesPerPixel: 4, + hasAlpha: true, + isPlanar: false, + colorSpaceName: NSCalibratedRGBColorSpace, + bytesPerRow: 0, + bitsPerPixel: 0) else + { + assertionFailure("[Kingfisher] Image representation cannot be created.") + return nil + } + rep.size = size + NSGraphicsContext.saveGraphicsState() + guard let context = NSGraphicsContext(bitmapImageRep: rep) else { + assertionFailure("[Kingfisher] Image contenxt cannot be created.") + return nil + } + + NSGraphicsContext.setCurrent(context) + return context.cgContext + #else + UIGraphicsBeginImageContextWithOptions(size, false, scale) + let context = UIGraphicsGetCurrentContext() + context?.scaleBy(x: 1.0, y: -1.0) + context?.translateBy(x: 0, y: -size.height) + return context + #endif + } + + func endContext() { + #if os(macOS) + NSGraphicsContext.restoreGraphicsState() + #else + UIGraphicsEndImageContext() + #endif + } + + func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image { + #if os(macOS) + guard let rep = NSBitmapImageRep( + bitmapDataPlanes: nil, + pixelsWide: Int(size.width), + pixelsHigh: Int(size.height), + bitsPerSample: cgImage?.bitsPerComponent ?? 8, + samplesPerPixel: 4, + hasAlpha: true, + isPlanar: false, + colorSpaceName: NSCalibratedRGBColorSpace, + bytesPerRow: 0, + bitsPerPixel: 0) else + { + assertionFailure("[Kingfisher] Image representation cannot be created.") + return base + } + rep.size = size + + NSGraphicsContext.saveGraphicsState() + + let context = NSGraphicsContext(bitmapImageRep: rep) + NSGraphicsContext.setCurrent(context) + draw() + NSGraphicsContext.restoreGraphicsState() + + let outputImage = Image(size: size) + outputImage.addRepresentation(rep) + return outputImage + #else + + UIGraphicsBeginImageContextWithOptions(size, false, scale) + defer { UIGraphicsEndImageContext() } + draw() + return UIGraphicsGetImageFromCurrentImageContext() ?? base + + #endif + } + + #if os(macOS) + func fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image { + + let image = Image(cgImage: cgImage, size: base.size) + let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) + + return draw(cgImage: cgImage, to: self.size) { + image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0) + } + } + #endif +} + +extension Float { + var isEven: Bool { + return truncatingRemainder(dividingBy: 2.0) == 0 + } +} + +// MARK: - Deprecated. Only for back compatibility. +extension Image { + /** + Normalize the image. This method does nothing in OS X. + + - returns: The image itself. + */ + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.normalized` instead.", + renamed: "kf.normalized") + public func kf_normalized() -> Image { + return kf.normalized + } + + // MARK: - Round Corner + + /// Create a round corner image based on `self`. + /// + /// - parameter radius: The round corner radius of creating image. + /// - parameter size: The target size of creating image. + /// - parameter scale: The image scale of creating image. + /// + /// - returns: An image with round corner of `self`. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.image(withRoundRadius:fit:scale:)` instead.", + renamed: "kf.image") + public func kf_image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { + return kf.image(withRoundRadius: radius, fit: size) + } + + // MARK: - Resize + /// Resize `self` to an image of new size. + /// + /// - parameter size: The target size. + /// + /// - returns: An image with new size. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.resize(to:)` instead.", + renamed: "kf.resize") + public func kf_resize(to size: CGSize) -> Image { + return kf.resize(to: size) + } + + // MARK: - Blur + /// Create an image with blur effect based on `self`. + /// + /// - parameter radius: The blur radius should be used when creating blue. + /// + /// - returns: An image with blur effect applied. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.blurred(withRadius:)` instead.", + renamed: "kf.blurred") + public func kf_blurred(withRadius radius: CGFloat) -> Image { + return kf.blurred(withRadius: radius) + } + + // MARK: - Overlay + /// Create an image from `self` with a color overlay layer. + /// + /// - parameter color: The color should be use to overlay. + /// - parameter fraction: Fraction of input color. From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. + /// + /// - returns: An image with a color overlay applied. + /// + /// - Note: This method only works for CG-based image. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.overlaying(with:fraction:)` instead.", + renamed: "kf.overlaying") + public func kf_overlaying(with color: Color, fraction: CGFloat) -> Image { + return kf.overlaying(with: color, fraction: fraction) + } + + // MARK: - Tint + + /// Create an image from `self` with a color tint. + /// + /// - parameter color: The color should be used to tint `self` + /// + /// - returns: An image with a color tint applied. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.tinted(with:)` instead.", + renamed: "kf.tinted") + public func kf_tinted(with color: Color) -> Image { + return kf.tinted(with: color) + } + + // MARK: - Color Control + + /// Create an image from `self` with color control. + /// + /// - parameter brightness: Brightness changing to image. + /// - parameter contrast: Contrast changing to image. + /// - parameter saturation: Saturation changing to image. + /// - parameter inputEV: InputEV changing to image. + /// + /// - returns: An image with color control applied. + @available(*, deprecated, + message: "Extensions directly on Image are deprecated. Use `kf.adjusted` instead.", + renamed: "kf.adjusted") + public func kf_adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { + return kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) + } +} + +extension Kingfisher where Base: Image { + @available(*, deprecated, + message: "`scale` is not used. Use the version without scale instead. (Remove the `scale` argument)") + public func image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { + return image(withRoundRadius: radius, fit: size) + } +} diff --git a/Pods/Kingfisher/Sources/ImageCache.swift b/Pods/Kingfisher/Sources/ImageCache.swift new file mode 100755 index 0000000..19f92ef --- /dev/null +++ b/Pods/Kingfisher/Sources/ImageCache.swift @@ -0,0 +1,693 @@ +// +// ImageCache.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +public extension Notification.Name { + /** + This notification will be sent when the disk cache got cleaned either there are cached files expired or the total size exceeding the max allowed size. The manually invoking of `clearDiskCache` method will not trigger this notification. + + The `object` of this notification is the `ImageCache` object which sends the notification. + + A list of removed hashes (files) could be retrieved by accessing the array under `KingfisherDiskCacheCleanedHashKey` key in `userInfo` of the notification object you received. By checking the array, you could know the hash codes of files are removed. + + The main purpose of this notification is supplying a chance to maintain some necessary information on the cached files. See [this wiki](https://github.com/onevcat/Kingfisher/wiki/How-to-implement-ETag-based-304-(Not-Modified)-handling-in-Kingfisher) for a use case on it. + */ + public static var KingfisherDidCleanDiskCache = Notification.Name.init("com.onevcat.Kingfisher.KingfisherDidCleanDiskCache") +} + +/** +Key for array of cleaned hashes in `userInfo` of `KingfisherDidCleanDiskCacheNotification`. +*/ +public let KingfisherDiskCacheCleanedHashKey = "com.onevcat.Kingfisher.cleanedHash" + +/// It represents a task of retrieving image. You can call `cancel` on it to stop the process. +public typealias RetrieveImageDiskTask = DispatchWorkItem + +/** +Cache type of a cached image. + +- None: The image is not cached yet when retrieving it. +- Memory: The image is cached in memory. +- Disk: The image is cached in disk. +*/ +public enum CacheType { + case none, memory, disk +} + +/// `ImageCache` represents both the memory and disk cache system of Kingfisher. +/// While a default image cache object will be used if you prefer the extension methods of Kingfisher, +/// you can create your own cache object and configure it as your need. You could use an `ImageCache` +/// object to manipulate memory and disk cache for Kingfisher. +open class ImageCache { + + //Memory + fileprivate let memoryCache = NSCache() + + /// The largest cache cost of memory cache. The total cost is pixel count of + /// all cached images in memory. + /// Default is unlimited. Memory cache will be purged automatically when a + /// memory warning notification is received. + open var maxMemoryCost: UInt = 0 { + didSet { + self.memoryCache.totalCostLimit = Int(maxMemoryCost) + } + } + + //Disk + fileprivate let ioQueue: DispatchQueue + fileprivate var fileManager: FileManager! + + ///The disk cache location. + open let diskCachePath: String + + /// The default file extension appended to cached files. + open var pathExtension: String? + + /// The longest time duration in second of the cache being stored in disk. + /// Default is 1 week (60 * 60 * 24 * 7 seconds). + /// Setting this to a negative value will make the disk cache never expiring. + open var maxCachePeriodInSecond: TimeInterval = 60 * 60 * 24 * 7 //Cache exists for 1 week + + /// The largest disk size can be taken for the cache. It is the total + /// allocated size of cached files in bytes. + /// Default is no limit. + open var maxDiskCacheSize: UInt = 0 + + fileprivate let processQueue: DispatchQueue + + /// The default cache. + public static let `default` = ImageCache(name: "default") + + /// Closure that defines the disk cache path from a given path and cacheName. + public typealias DiskCachePathClosure = (String?, String) -> String + + /// The default DiskCachePathClosure + public final class func defaultDiskCachePathClosure(path: String?, cacheName: String) -> String { + let dstPath = path ?? NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! + return (dstPath as NSString).appendingPathComponent(cacheName) + } + + /** + Init method. Passing a name for the cache. It represents a cache folder in the memory and disk. + + - parameter name: Name of the cache. It will be used as the memory cache name and the disk cache folder name + appending to the cache path. This value should not be an empty string. + - parameter path: Optional - Location of cache path on disk. If `nil` is passed in (the default value), + the `.cachesDirectory` in of your app will be used. + - parameter diskCachePathClosure: Closure that takes in an optional initial path string and generates + the final disk cache path. You could use it to fully customize your cache path. + + - returns: The cache object. + */ + public init(name: String, + path: String? = nil, + diskCachePathClosure: DiskCachePathClosure = ImageCache.defaultDiskCachePathClosure) + { + + if name.isEmpty { + fatalError("[Kingfisher] You should specify a name for the cache. A cache with empty name is not permitted.") + } + + let cacheName = "com.onevcat.Kingfisher.ImageCache.\(name)" + memoryCache.name = cacheName + + diskCachePath = diskCachePathClosure(path, cacheName) + + let ioQueueName = "com.onevcat.Kingfisher.ImageCache.ioQueue.\(name)" + ioQueue = DispatchQueue(label: ioQueueName) + + let processQueueName = "com.onevcat.Kingfisher.ImageCache.processQueue.\(name)" + processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent) + + ioQueue.sync { fileManager = FileManager() } + +#if !os(macOS) && !os(watchOS) + NotificationCenter.default.addObserver( + self, selector: #selector(clearMemoryCache), name: .UIApplicationDidReceiveMemoryWarning, object: nil) + NotificationCenter.default.addObserver( + self, selector: #selector(cleanExpiredDiskCache), name: .UIApplicationWillTerminate, object: nil) + NotificationCenter.default.addObserver( + self, selector: #selector(backgroundCleanExpiredDiskCache), name: .UIApplicationDidEnterBackground, object: nil) +#endif + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + + // MARK: - Store & Remove + + /** + Store an image to cache. It will be saved to both memory and disk. It is an async operation. + + - parameter image: The image to be stored. + - parameter original: The original data of the image. + Kingfisher will use it to check the format of the image and optimize cache size on disk. + If `nil` is supplied, the image data will be saved as a normalized PNG file. + It is strongly suggested to supply it whenever possible, to get a better performance and disk usage. + - parameter key: Key for the image. + - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of + processor to it. + This identifier will be used to generate a corresponding key for the combination of `key` and processor. + - parameter toDisk: Whether this image should be cached to disk or not. If false, the image will be only cached in memory. + - parameter completionHandler: Called when store operation completes. + */ + open func store(_ image: Image, + original: Data? = nil, + forKey key: String, + processorIdentifier identifier: String = "", + cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default, + toDisk: Bool = true, + completionHandler: (() -> Void)? = nil) + { + + let computedKey = key.computedKey(with: identifier) + memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf.imageCost) + + func callHandlerInMainQueue() { + if let handler = completionHandler { + DispatchQueue.main.async { + handler() + } + } + } + + if toDisk { + ioQueue.async { + + if let data = serializer.data(with: image, original: original) { + if !self.fileManager.fileExists(atPath: self.diskCachePath) { + do { + try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil) + } catch _ {} + } + + self.fileManager.createFile(atPath: self.cachePath(forComputedKey: computedKey), contents: data, attributes: nil) + } + callHandlerInMainQueue() + } + } else { + callHandlerInMainQueue() + } + } + + /** + Remove the image for key for the cache. It will be opted out from both memory and disk. + It is an async operation. + + - parameter key: Key for the image. + - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. + This identifier will be used to generate a corresponding key for the combination of `key` and processor. + - parameter fromDisk: Whether this image should be removed from disk or not. If false, the image will be only removed from memory. + - parameter completionHandler: Called when removal operation completes. + */ + open func removeImage(forKey key: String, + processorIdentifier identifier: String = "", + fromDisk: Bool = true, + completionHandler: (() -> Void)? = nil) + { + let computedKey = key.computedKey(with: identifier) + memoryCache.removeObject(forKey: computedKey as NSString) + + func callHandlerInMainQueue() { + if let handler = completionHandler { + DispatchQueue.main.async { + handler() + } + } + } + + if fromDisk { + ioQueue.async{ + do { + try self.fileManager.removeItem(atPath: self.cachePath(forComputedKey: computedKey)) + } catch _ {} + callHandlerInMainQueue() + } + } else { + callHandlerInMainQueue() + } + } + + // MARK: - Get data from cache + + /** + Get an image for a key from memory or disk. + + - parameter key: Key for the image. + - parameter options: Options of retrieving image. If you need to retrieve an image which was + stored with a specified `ImageProcessor`, pass the processor in the option too. + - parameter completionHandler: Called when getting operation completes with image result and cached type of + this image. If there is no such key cached, the image will be `nil`. + + - returns: The retrieving task. + */ + @discardableResult + open func retrieveImage(forKey key: String, + options: KingfisherOptionsInfo?, + completionHandler: ((Image?, CacheType) -> ())?) -> RetrieveImageDiskTask? + { + // No completion handler. Not start working and early return. + guard let completionHandler = completionHandler else { + return nil + } + + var block: RetrieveImageDiskTask? + let options = options ?? KingfisherEmptyOptionsInfo + + if let image = self.retrieveImageInMemoryCache(forKey: key, options: options) { + options.callbackDispatchQueue.safeAsync { + completionHandler(image, .memory) + } + } else { + var sSelf: ImageCache! = self + block = DispatchWorkItem(block: { + // Begin to load image from disk + if let image = sSelf.retrieveImageInDiskCache(forKey: key, options: options) { + if options.backgroundDecode { + sSelf.processQueue.async { + let result = image.kf.decoded(scale: options.scaleFactor) + + sSelf.store(result, + forKey: key, + processorIdentifier: options.processor.identifier, + cacheSerializer: options.cacheSerializer, + toDisk: false, + completionHandler: nil) + + options.callbackDispatchQueue.safeAsync { + completionHandler(result, .memory) + sSelf = nil + } + } + } else { + sSelf.store(image, + forKey: key, + processorIdentifier: options.processor.identifier, + cacheSerializer: options.cacheSerializer, + toDisk: false, + completionHandler: nil + ) + options.callbackDispatchQueue.safeAsync { + completionHandler(image, .disk) + sSelf = nil + } + } + } else { + // No image found from either memory or disk + options.callbackDispatchQueue.safeAsync { + completionHandler(nil, .none) + sSelf = nil + } + } + }) + + sSelf.ioQueue.async(execute: block!) + } + + return block + } + + /** + Get an image for a key from memory. + + - parameter key: Key for the image. + - parameter options: Options of retrieving image. If you need to retrieve an image which was + stored with a specified `ImageProcessor`, pass the processor in the option too. + - returns: The image object if it is cached, or `nil` if there is no such key in the cache. + */ + open func retrieveImageInMemoryCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? { + + let options = options ?? KingfisherEmptyOptionsInfo + let computedKey = key.computedKey(with: options.processor.identifier) + + return memoryCache.object(forKey: computedKey as NSString) as? Image + } + + /** + Get an image for a key from disk. + + - parameter key: Key for the image. + - parameter options: Options of retrieving image. If you need to retrieve an image which was + stored with a specified `ImageProcessor`, pass the processor in the option too. + + - returns: The image object if it is cached, or `nil` if there is no such key in the cache. + */ + open func retrieveImageInDiskCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? { + + let options = options ?? KingfisherEmptyOptionsInfo + let computedKey = key.computedKey(with: options.processor.identifier) + + return diskImage(forComputedKey: computedKey, serializer: options.cacheSerializer, options: options) + } + + + // MARK: - Clear & Clean + + /** + Clear memory cache. + */ + @objc public func clearMemoryCache() { + memoryCache.removeAllObjects() + } + + /** + Clear disk cache. This is an async operation. + + - parameter completionHander: Called after the operation completes. + */ + open func clearDiskCache(completion handler: (()->())? = nil) { + ioQueue.async { + do { + try self.fileManager.removeItem(atPath: self.diskCachePath) + try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil) + } catch _ { } + + if let handler = handler { + DispatchQueue.main.async { + handler() + } + } + } + } + + /** + Clean expired disk cache. This is an async operation. + */ + @objc fileprivate func cleanExpiredDiskCache() { + cleanExpiredDiskCache(completion: nil) + } + + /** + Clean expired disk cache. This is an async operation. + + - parameter completionHandler: Called after the operation completes. + */ + open func cleanExpiredDiskCache(completion handler: (()->())? = nil) { + + // Do things in cocurrent io queue + ioQueue.async { + + var (URLsToDelete, diskCacheSize, cachedFiles) = self.travelCachedFiles(onlyForCacheSize: false) + + for fileURL in URLsToDelete { + do { + try self.fileManager.removeItem(at: fileURL) + } catch _ { } + } + + if self.maxDiskCacheSize > 0 && diskCacheSize > self.maxDiskCacheSize { + let targetSize = self.maxDiskCacheSize / 2 + + // Sort files by last modify date. We want to clean from the oldest files. + let sortedFiles = cachedFiles.keysSortedByValue { + resourceValue1, resourceValue2 -> Bool in + + if let date1 = resourceValue1.contentAccessDate, + let date2 = resourceValue2.contentAccessDate + { + return date1.compare(date2) == .orderedAscending + } + + // Not valid date information. This should not happen. Just in case. + return true + } + + for fileURL in sortedFiles { + + do { + try self.fileManager.removeItem(at: fileURL) + } catch { } + + URLsToDelete.append(fileURL) + + if let fileSize = cachedFiles[fileURL]?.totalFileAllocatedSize { + diskCacheSize -= UInt(fileSize) + } + + if diskCacheSize < targetSize { + break + } + } + } + + DispatchQueue.main.async { + + if URLsToDelete.count != 0 { + let cleanedHashes = URLsToDelete.map { $0.lastPathComponent } + NotificationCenter.default.post(name: .KingfisherDidCleanDiskCache, object: self, userInfo: [KingfisherDiskCacheCleanedHashKey: cleanedHashes]) + } + + handler?() + } + } + } + + fileprivate func travelCachedFiles(onlyForCacheSize: Bool) -> (urlsToDelete: [URL], diskCacheSize: UInt, cachedFiles: [URL: URLResourceValues]) { + + let diskCacheURL = URL(fileURLWithPath: diskCachePath) + let resourceKeys: Set = [.isDirectoryKey, .contentAccessDateKey, .totalFileAllocatedSizeKey] + let expiredDate: Date? = (maxCachePeriodInSecond < 0) ? nil : Date(timeIntervalSinceNow: -maxCachePeriodInSecond) + + var cachedFiles = [URL: URLResourceValues]() + var urlsToDelete = [URL]() + var diskCacheSize: UInt = 0 + + if let fileEnumerator = self.fileManager.enumerator(at: diskCacheURL, includingPropertiesForKeys: Array(resourceKeys), options: FileManager.DirectoryEnumerationOptions.skipsHiddenFiles, errorHandler: nil), + let urls = fileEnumerator.allObjects as? [URL] + { + for fileUrl in urls { + + do { + let resourceValues = try fileUrl.resourceValues(forKeys: resourceKeys) + // If it is a Directory. Continue to next file URL. + if resourceValues.isDirectory == true { + continue + } + + // If this file is expired, add it to URLsToDelete + if !onlyForCacheSize, + let expiredDate = expiredDate, + let lastAccessData = resourceValues.contentAccessDate, + (lastAccessData as NSDate).laterDate(expiredDate) == expiredDate + { + urlsToDelete.append(fileUrl) + continue + } + + if let fileSize = resourceValues.totalFileAllocatedSize { + diskCacheSize += UInt(fileSize) + if !onlyForCacheSize { + cachedFiles[fileUrl] = resourceValues + } + } + } catch _ { } + } + } + + return (urlsToDelete, diskCacheSize, cachedFiles) + } + +#if !os(macOS) && !os(watchOS) + /** + Clean expired disk cache when app in background. This is an async operation. + In most cases, you should not call this method explicitly. + It will be called automatically when `UIApplicationDidEnterBackgroundNotification` received. + */ + @objc public func backgroundCleanExpiredDiskCache() { + // if 'sharedApplication()' is unavailable, then return + guard let sharedApplication = Kingfisher.shared else { return } + + func endBackgroundTask(_ task: inout UIBackgroundTaskIdentifier) { + sharedApplication.endBackgroundTask(task) + task = UIBackgroundTaskInvalid + } + + var backgroundTask: UIBackgroundTaskIdentifier! + backgroundTask = sharedApplication.beginBackgroundTask { + endBackgroundTask(&backgroundTask!) + } + + cleanExpiredDiskCache { + endBackgroundTask(&backgroundTask!) + } + } +#endif + + + // MARK: - Check cache status + + /** + * Cache result for checking whether an image is cached for a key. + */ + public struct CacheCheckResult { + public let cached: Bool + public let cacheType: CacheType? + } + + /** + Check whether an image is cached for a key. + + - parameter key: Key for the image. + + - returns: The check result. + */ + open func isImageCached(forKey key: String, processorIdentifier identifier: String = "") -> CacheCheckResult { + + let computedKey = key.computedKey(with: identifier) + + if memoryCache.object(forKey: computedKey as NSString) != nil { + return CacheCheckResult(cached: true, cacheType: .memory) + } + + let filePath = cachePath(forComputedKey: computedKey) + + var diskCached = false + ioQueue.sync { + diskCached = fileManager.fileExists(atPath: filePath) + } + + if diskCached { + return CacheCheckResult(cached: true, cacheType: .disk) + } + + return CacheCheckResult(cached: false, cacheType: nil) + } + + /** + Get the hash for the key. This could be used for matching files. + + - parameter key: The key which is used for caching. + - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it. + + - returns: Corresponding hash. + */ + open func hash(forKey key: String, processorIdentifier identifier: String = "") -> String { + let computedKey = key.computedKey(with: identifier) + return cacheFileName(forComputedKey: computedKey) + } + + /** + Calculate the disk size taken by cache. + It is the total allocated size of the cached files in bytes. + + - parameter completionHandler: Called with the calculated size when finishes. + */ + open func calculateDiskCacheSize(completion handler: @escaping ((_ size: UInt) -> ())) { + ioQueue.async { + let (_, diskCacheSize, _) = self.travelCachedFiles(onlyForCacheSize: true) + DispatchQueue.main.async { + handler(diskCacheSize) + } + } + } + + /** + Get the cache path for the key. + It is useful for projects with UIWebView or anyone that needs access to the local file path. + + i.e. Replace the `` tag in your HTML. + + - Note: This method does not guarantee there is an image already cached in the path. It just returns the path + that the image should be. + You could use `isImageCached(forKey:)` method to check whether the image is cached under that key. + */ + open func cachePath(forKey key: String, processorIdentifier identifier: String = "") -> String { + let computedKey = key.computedKey(with: identifier) + return cachePath(forComputedKey: computedKey) + } + + open func cachePath(forComputedKey key: String) -> String { + let fileName = cacheFileName(forComputedKey: key) + return (diskCachePath as NSString).appendingPathComponent(fileName) + } +} + +// MARK: - Internal Helper +extension ImageCache { + + func diskImage(forComputedKey key: String, serializer: CacheSerializer, options: KingfisherOptionsInfo) -> Image? { + if let data = diskImageData(forComputedKey: key) { + return serializer.image(with: data, options: options) + } else { + return nil + } + } + + func diskImageData(forComputedKey key: String) -> Data? { + let filePath = cachePath(forComputedKey: key) + return (try? Data(contentsOf: URL(fileURLWithPath: filePath))) + } + + func cacheFileName(forComputedKey key: String) -> String { + if let ext = self.pathExtension { + return (key.kf.md5 as NSString).appendingPathExtension(ext)! + } + return key.kf.md5 + } +} + +extension Kingfisher where Base: Image { + var imageCost: Int { + return images == nil ? + Int(size.height * size.width * scale * scale) : + Int(size.height * size.width * scale * scale) * images!.count + } +} + +extension Dictionary { + func keysSortedByValue(_ isOrderedBefore: (Value, Value) -> Bool) -> [Key] { + return Array(self).sorted{ isOrderedBefore($0.1, $1.1) }.map{ $0.0 } + } +} + +#if !os(macOS) && !os(watchOS) +// MARK: - For App Extensions +extension UIApplication: KingfisherCompatible { } +extension Kingfisher where Base: UIApplication { + public static var shared: UIApplication? { + let selector = NSSelectorFromString("sharedApplication") + guard Base.responds(to: selector) else { return nil } + return Base.perform(selector).takeUnretainedValue() as? UIApplication + } +} +#endif + +extension String { + func computedKey(with identifier: String) -> String { + if identifier.isEmpty { + return self + } else { + return appending("@\(identifier)") + } + } +} diff --git a/Pods/Kingfisher/Sources/ImageDownloader.swift b/Pods/Kingfisher/Sources/ImageDownloader.swift new file mode 100755 index 0000000..46039b4 --- /dev/null +++ b/Pods/Kingfisher/Sources/ImageDownloader.swift @@ -0,0 +1,532 @@ +// +// ImageDownloader.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +/// Progress update block of downloader. +public typealias ImageDownloaderProgressBlock = DownloadProgressBlock + +/// Completion block of downloader. +public typealias ImageDownloaderCompletionHandler = ((_ image: Image?, _ error: NSError?, _ url: URL?, _ originalData: Data?) -> ()) + +/// Download task. +public struct RetrieveImageDownloadTask { + let internalTask: URLSessionDataTask + + /// Downloader by which this task is intialized. + public private(set) weak var ownerDownloader: ImageDownloader? + + /** + Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error. + */ + public func cancel() { + ownerDownloader?.cancelDownloadingTask(self) + } + + /// The original request URL of this download task. + public var url: URL? { + return internalTask.originalRequest?.url + } + + /// The relative priority of this download task. + /// It represents the `priority` property of the internal `NSURLSessionTask` of this download task. + /// The value for it is between 0.0~1.0. Default priority is value of 0.5. + /// See documentation on `priority` of `NSURLSessionTask` for more about it. + public var priority: Float { + get { + return internalTask.priority + } + set { + internalTask.priority = newValue + } + } +} + +///The code of errors which `ImageDownloader` might encountered. +public enum KingfisherError: Int { + + /// badData: The downloaded data is not an image or the data is corrupted. + case badData = 10000 + + /// notModified: The remote server responsed a 304 code. No image data downloaded. + case notModified = 10001 + + /// The HTTP status code in response is not valid. If an invalid + /// code error received, you could check the value under `KingfisherErrorStatusCodeKey` + /// in `userInfo` to see the code. + case invalidStatusCode = 10002 + + /// notCached: The image rquested is not in cache but .onlyFromCache is activated. + case notCached = 10003 + + /// The URL is invalid. + case invalidURL = 20000 + + /// The downloading task is cancelled before started. + case downloadCancelledBeforeStarting = 30000 +} + +/// Key will be used in the `userInfo` of `.invalidStatusCode` +public let KingfisherErrorStatusCodeKey = "statusCode" + +/// Protocol of `ImageDownloader`. +public protocol ImageDownloaderDelegate: class { + /** + Called when the `ImageDownloader` object successfully downloaded an image from specified URL. + + - parameter downloader: The `ImageDownloader` object finishes the downloading. + - parameter image: Downloaded image. + - parameter url: URL of the original request URL. + - parameter response: The response object of the downloading process. + */ + func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) + + + /** + Check if a received HTTP status code is valid or not. + By default, a status code between 200 to 400 (excluded) is considered as valid. + If an invalid code is received, the downloader will raise an .invalidStatusCode error. + It has a `userInfo` which includes this statusCode and localizedString error message. + + - parameter code: The received HTTP status code. + - parameter downloader: The `ImageDownloader` object asking for validate status code. + + - returns: Whether this HTTP status code is valid or not. + + - Note: If the default 200 to 400 valid code does not suit your need, + you can implement this method to change that behavior. + */ + func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool +} + +extension ImageDownloaderDelegate { + public func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) {} + + public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool { + return (200..<400).contains(code) + } +} + +/// Protocol indicates that an authentication challenge could be handled. +public protocol AuthenticationChallengeResponsable: class { + /** + Called when an session level authentication challenge is received. + This method provide a chance to handle and response to the authentication challenge before downloading could start. + + - parameter downloader: The downloader which receives this challenge. + - parameter challenge: An object that contains the request for authentication. + - parameter completionHandler: A handler that your delegate method must call. + + - Note: This method is a forward from `URLSession(:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `NSURLSessionDelegate`. + */ + func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) +} + +extension AuthenticationChallengeResponsable { + + func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + + if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { + if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) { + let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) + completionHandler(.useCredential, credential) + return + } + } + + completionHandler(.performDefaultHandling, nil) + } +} + +/// `ImageDownloader` represents a downloading manager for requesting the image with a URL from server. +open class ImageDownloader { + + class ImageFetchLoad { + var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]() + var responseData = NSMutableData() + + var downloadTaskCount = 0 + var downloadTask: RetrieveImageDownloadTask? + } + + // MARK: - Public property + /// The duration before the download is timeout. Default is 15 seconds. + open var downloadTimeout: TimeInterval = 15.0 + + /// A set of trusted hosts when receiving server trust challenges. A challenge with host name contained in this set will be ignored. + /// You can use this set to specify the self-signed site. It only will be used if you don't specify the `authenticationChallengeResponder`. + /// If `authenticationChallengeResponder` is set, this property will be ignored and the implemention of `authenticationChallengeResponder` will be used instead. + open var trustedHosts: Set? + + /// Use this to set supply a configuration for the downloader. By default, NSURLSessionConfiguration.ephemeralSessionConfiguration() will be used. + /// You could change the configuration before a downloaing task starts. A configuration without persistent storage for caches is requsted for downloader working correctly. + open var sessionConfiguration = URLSessionConfiguration.ephemeral { + didSet { + session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: OperationQueue.main) + } + } + + /// Whether the download requests should use pipeling or not. Default is false. + open var requestsUsePipeling = false + + fileprivate let sessionHandler: ImageDownloaderSessionHandler + fileprivate var session: URLSession? + + /// Delegate of this `ImageDownloader` object. See `ImageDownloaderDelegate` protocol for more. + open weak var delegate: ImageDownloaderDelegate? + + /// A responder for authentication challenge. + /// Downloader will forward the received authentication challenge for the downloading session to this responder. + open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable? + + // MARK: - Internal property + let barrierQueue: DispatchQueue + let processQueue: DispatchQueue + + typealias CallbackPair = (progressBlock: ImageDownloaderProgressBlock?, completionHandler: ImageDownloaderCompletionHandler?) + + var fetchLoads = [URL: ImageFetchLoad]() + + // MARK: - Public method + /// The default downloader. + public static let `default` = ImageDownloader(name: "default") + + /** + Init a downloader with name. + + - parameter name: The name for the downloader. It should not be empty. + + - returns: The downloader object. + */ + public init(name: String) { + if name.isEmpty { + fatalError("[Kingfisher] You should specify a name for the downloader. A downloader with empty name is not permitted.") + } + + barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent) + processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent) + + sessionHandler = ImageDownloaderSessionHandler() + + // Provide a default implement for challenge responder. + authenticationChallengeResponder = sessionHandler + session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: .main) + } + + func fetchLoad(for url: URL) -> ImageFetchLoad? { + var fetchLoad: ImageFetchLoad? + barrierQueue.sync { fetchLoad = fetchLoads[url] } + return fetchLoad + } + + /** + Download an image with a URL and option. + + - parameter url: Target URL. + - parameter options: The options could control download behavior. See `KingfisherOptionsInfo`. + - parameter progressBlock: Called when the download progress updated. + - parameter completionHandler: Called when the download progress finishes. + + - returns: A downloading task. You could call `cancel` on it to stop the downloading process. + */ + @discardableResult + open func downloadImage(with url: URL, + options: KingfisherOptionsInfo? = nil, + progressBlock: ImageDownloaderProgressBlock? = nil, + completionHandler: ImageDownloaderCompletionHandler? = nil) -> RetrieveImageDownloadTask? + { + return downloadImage(with: url, + retrieveImageTask: nil, + options: options, + progressBlock: progressBlock, + completionHandler: completionHandler) + } +} + +// MARK: - Download method +extension ImageDownloader { + func downloadImage(with url: URL, + retrieveImageTask: RetrieveImageTask?, + options: KingfisherOptionsInfo?, + progressBlock: ImageDownloaderProgressBlock?, + completionHandler: ImageDownloaderCompletionHandler?) -> RetrieveImageDownloadTask? + { + if let retrieveImageTask = retrieveImageTask, retrieveImageTask.cancelledBeforeDownloadStarting { + completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil) + return nil + } + + let timeout = self.downloadTimeout == 0.0 ? 15.0 : self.downloadTimeout + + // We need to set the URL as the load key. So before setup progress, we need to ask the `requestModifier` for a final URL. + var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeout) + request.httpShouldUsePipelining = requestsUsePipeling + + if let modifier = options?.modifier { + guard let r = modifier.modified(for: request) else { + completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil) + return nil + } + request = r + } + + // There is a possiblility that request modifier changed the url to `nil` or empty. + guard let url = request.url, !url.absoluteString.isEmpty else { + completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidURL.rawValue, userInfo: nil), nil, nil) + return nil + } + + var downloadTask: RetrieveImageDownloadTask? + setup(progressBlock: progressBlock, with: completionHandler, for: url, options: options) {(session, fetchLoad) -> Void in + if fetchLoad.downloadTask == nil { + let dataTask = session.dataTask(with: request) + + fetchLoad.downloadTask = RetrieveImageDownloadTask(internalTask: dataTask, ownerDownloader: self) + + dataTask.priority = options?.downloadPriority ?? URLSessionTask.defaultPriority + dataTask.resume() + + // Hold self while the task is executing. + self.sessionHandler.downloadHolder = self + } + + fetchLoad.downloadTaskCount += 1 + downloadTask = fetchLoad.downloadTask + + retrieveImageTask?.downloadTask = downloadTask + } + return downloadTask + } + + // A single key may have multiple callbacks. Only download once. + func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: ((URLSession, ImageFetchLoad) -> Void)) { + + barrierQueue.sync(flags: .barrier) { + let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad() + let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler) + + loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo)) + + fetchLoads[url] = loadObjectForURL + + if let session = session { + started(session, loadObjectForURL) + } + } + } + + func cancelDownloadingTask(_ task: RetrieveImageDownloadTask) { + barrierQueue.sync { + if let URL = task.internalTask.originalRequest?.url, let imageFetchLoad = self.fetchLoads[URL] { + imageFetchLoad.downloadTaskCount -= 1 + if imageFetchLoad.downloadTaskCount == 0 { + task.internalTask.cancel() + } + } + } + } + + func clean(for url: URL) { + barrierQueue.sync(flags: .barrier) { + fetchLoads.removeValue(forKey: url) + return + } + } +} + +// MARK: - NSURLSessionDataDelegate + +/// Delegate class for `NSURLSessionTaskDelegate`. +/// The session object will hold its delegate until it gets invalidated. +/// If we use `ImageDownloader` as the session delegate, it will not be released. +/// So we need an additional handler to break the retain cycle. +// See https://github.com/onevcat/Kingfisher/issues/235 +class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, AuthenticationChallengeResponsable { + + // The holder will keep downloader not released while a data task is being executed. + // It will be set when the task started, and reset when the task finished. + var downloadHolder: ImageDownloader? + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { + + guard let downloader = downloadHolder else { + completionHandler(.cancel) + return + } + + if let statusCode = (response as? HTTPURLResponse)?.statusCode, + let url = dataTask.originalRequest?.url, + !(downloader.delegate ?? downloader).isValidStatusCode(statusCode, for: downloader) + { + let error = NSError(domain: KingfisherErrorDomain, + code: KingfisherError.invalidStatusCode.rawValue, + userInfo: [KingfisherErrorStatusCodeKey: statusCode, NSLocalizedDescriptionKey: HTTPURLResponse.localizedString(forStatusCode: statusCode)]) + callCompletionHandlerFailure(error: error, url: url) + } + + completionHandler(.allow) + } + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + + guard let downloader = downloadHolder else { + return + } + + if let url = dataTask.originalRequest?.url, let fetchLoad = downloader.fetchLoad(for: url) { + fetchLoad.responseData.append(data) + + if let expectedLength = dataTask.response?.expectedContentLength { + for content in fetchLoad.contents { + DispatchQueue.main.async { + content.callback.progressBlock?(Int64(fetchLoad.responseData.length), expectedLength) + } + } + } + } + } + + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + + guard let url = task.originalRequest?.url else { + return + } + + guard error == nil else { + callCompletionHandlerFailure(error: error!, url: url) + return + } + + processImage(for: task, url: url) + } + + /** + This method is exposed since the compiler requests. Do not call it. + */ + func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + guard let downloader = downloadHolder else { + return + } + + downloader.authenticationChallengeResponder?.downloader(downloader, didReceive: challenge, completionHandler: completionHandler) + } + + private func cleanFetchLoad(for url: URL) { + guard let downloader = downloadHolder else { + return + } + + downloader.clean(for: url) + + if downloader.fetchLoads.isEmpty { + downloadHolder = nil + } + } + + private func callCompletionHandlerFailure(error: Error, url: URL) { + guard let downloader = downloadHolder, let fetchLoad = downloader.fetchLoad(for: url) else { + return + } + + // We need to clean the fetch load first, before actually calling completion handler. + cleanFetchLoad(for: url) + + for content in fetchLoad.contents { + content.options.callbackDispatchQueue.safeAsync { + content.callback.completionHandler?(nil, error as NSError, url, nil) + } + } + } + + private func processImage(for task: URLSessionTask, url: URL) { + + guard let downloader = downloadHolder else { + return + } + + // We are on main queue when receiving this. + downloader.processQueue.async { + + guard let fetchLoad = downloader.fetchLoad(for: url) else { + return + } + + self.cleanFetchLoad(for: url) + + let data = fetchLoad.responseData as Data + + // Cache the processed images. So we do not need to re-process the image if using the same processor. + // Key is the identifier of processor. + var imageCache: [String: Image] = [:] + for content in fetchLoad.contents { + + let options = content.options + let completionHandler = content.callback.completionHandler + let callbackQueue = options.callbackDispatchQueue + + let processor = options.processor + + var image = imageCache[processor.identifier] + if image == nil { + image = processor.process(item: .data(data), options: options) + + // Add the processed image to cache. + // If `image` is nil, nothing will happen (since the key is not existing before). + imageCache[processor.identifier] = image + } + + if let image = image { + + downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response) + + if options.backgroundDecode { + let decodedImage = image.kf.decoded(scale: options.scaleFactor) + callbackQueue.safeAsync { completionHandler?(decodedImage, nil, url, data) } + } else { + callbackQueue.safeAsync { completionHandler?(image, nil, url, data) } + } + + } else { + if let res = task.response as? HTTPURLResponse , res.statusCode == 304 { + let notModified = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notModified.rawValue, userInfo: nil) + completionHandler?(nil, notModified, url, nil) + continue + } + + let badData = NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil) + callbackQueue.safeAsync { completionHandler?(nil, badData, url, nil) } + } + } + } + } +} + +// Placeholder. For retrieving extension methods of ImageDownloaderDelegate +extension ImageDownloader: ImageDownloaderDelegate {} diff --git a/Pods/Kingfisher/Sources/ImagePrefetcher.swift b/Pods/Kingfisher/Sources/ImagePrefetcher.swift new file mode 100755 index 0000000..b7c0a21 --- /dev/null +++ b/Pods/Kingfisher/Sources/ImagePrefetcher.swift @@ -0,0 +1,269 @@ +// +// ImagePrefetcher.swift +// Kingfisher +// +// Created by Claire Knight on 24/02/2016 +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#if os(macOS) + import AppKit +#else + import UIKit +#endif + + +/// Progress update block of prefetcher. +/// +/// - `skippedResources`: An array of resources that are already cached before the prefetching starting. +/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. +/// - `completedResources`: An array of resources that are downloaded and cached successfully. +public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) + +/// Completion block of prefetcher. +/// +/// - `skippedResources`: An array of resources that are already cached before the prefetching starting. +/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all. +/// - `completedResources`: An array of resources that are downloaded and cached successfully. +public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> ()) + +/// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them. +/// This is useful when you know a list of image resources and want to download them before showing. +public class ImagePrefetcher { + + /// The maximum concurrent downloads to use when prefetching images. Default is 5. + public var maxConcurrentDownloads = 5 + + private let prefetchResources: [Resource] + private let optionsInfo: KingfisherOptionsInfo + private var progressBlock: PrefetcherProgressBlock? + private var completionHandler: PrefetcherCompletionHandler? + + private var tasks = [URL: RetrieveImageDownloadTask]() + + private var pendingResources: ArraySlice + private var skippedResources = [Resource]() + private var completedResources = [Resource]() + private var failedResources = [Resource]() + + private var stopped = false + + // The created manager used for prefetch. We will use the helper method in manager. + private let manager: KingfisherManager + + private var finished: Bool { + return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty + } + + /** + Init an image prefetcher with an array of URLs. + + The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. + After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. + The images already cached will be skipped without downloading again. + + - parameter urls: The URLs which should be prefetched. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. + - parameter completionHandler: Called when the whole prefetching process finished. + + - returns: An `ImagePrefetcher` object. + + - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as + the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. + Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. + */ + public convenience init(urls: [URL], + options: KingfisherOptionsInfo? = nil, + progressBlock: PrefetcherProgressBlock? = nil, + completionHandler: PrefetcherCompletionHandler? = nil) + { + let resources: [Resource] = urls.map { $0 } + self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Init an image prefetcher with an array of resources. + + The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable. + After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process. + The images already cached will be skipped without downloading again. + + - parameter resources: The resources which should be prefetched. See `Resource` type for more. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled. + - parameter completionHandler: Called when the whole prefetching process finished. + + - returns: An `ImagePrefetcher` object. + + - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as + the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`. + Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method. + */ + public init(resources: [Resource], + options: KingfisherOptionsInfo? = nil, + progressBlock: PrefetcherProgressBlock? = nil, + completionHandler: PrefetcherCompletionHandler? = nil) + { + prefetchResources = resources + pendingResources = ArraySlice(resources) + + // We want all callbacks from main queue, so we ignore the call back queue in options + let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) + self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo + + let cache = self.optionsInfo.targetCache + let downloader = self.optionsInfo.downloader + manager = KingfisherManager(downloader: downloader, cache: cache) + + self.progressBlock = progressBlock + self.completionHandler = completionHandler + } + + /** + Start to download the resources and cache them. This can be useful for background downloading + of assets that are required for later use in an app. This code will not try and update any UI + with the results of the process. + */ + public func start() + { + // Since we want to handle the resources cancellation in main thread only. + DispatchQueue.main.safeAsync { + + guard !self.stopped else { + assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.") + self.handleComplete() + return + } + + guard self.maxConcurrentDownloads > 0 else { + assertionFailure("There should be concurrent downloads value should be at least 1.") + self.handleComplete() + return + } + + guard self.prefetchResources.count > 0 else { + self.handleComplete() + return + } + + let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads) + for _ in 0 ..< initialConcurentDownloads { + if let resource = self.pendingResources.popFirst() { + self.startPrefetching(resource) + } + } + } + } + + + /** + Stop current downloading progress, and cancel any future prefetching activity that might be occuring. + */ + public func stop() { + DispatchQueue.main.safeAsync { + + if self.finished { return } + + self.stopped = true + self.tasks.forEach { (_, task) -> () in + task.cancel() + } + } + } + + func downloadAndCache(_ resource: Resource) { + + let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> () in + self.tasks.removeValue(forKey: resource.downloadURL) + if let _ = error { + self.failedResources.append(resource) + } else { + self.completedResources.append(resource) + } + + self.reportProgress() + if self.stopped { + if self.tasks.isEmpty { + self.failedResources.append(contentsOf: self.pendingResources) + self.handleComplete() + } + } else { + self.reportCompletionOrStartNext() + } + } + + let downloadTask = manager.downloadAndCacheImage( + with: resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: RetrieveImageTask(), + progressBlock: nil, + completionHandler: downloadTaskCompletionHandler, + options: optionsInfo) + + if let downloadTask = downloadTask { + tasks[resource.downloadURL] = downloadTask + } + } + + func append(cached resource: Resource) { + skippedResources.append(resource) + + reportProgress() + reportCompletionOrStartNext() + } + + func startPrefetching(_ resource: Resource) + { + if optionsInfo.forceRefresh { + downloadAndCache(resource) + } else { + let alreadyInCache = manager.cache.isImageCached(forKey: resource.cacheKey, + processorIdentifier: optionsInfo.processor.identifier).cached + + if alreadyInCache { + append(cached: resource) + } else { + downloadAndCache(resource) + } + } + } + + func reportProgress() { + progressBlock?(skippedResources, failedResources, completedResources) + } + + func reportCompletionOrStartNext() { + if let resource = pendingResources.popFirst() { + startPrefetching(resource) + } else { + guard tasks.isEmpty else { return } + handleComplete() + } + } + + func handleComplete() { + completionHandler?(skippedResources, failedResources, completedResources) + completionHandler = nil + progressBlock = nil + } +} diff --git a/Pods/Kingfisher/Sources/ImageProcessor.swift b/Pods/Kingfisher/Sources/ImageProcessor.swift new file mode 100644 index 0000000..48d0c26 --- /dev/null +++ b/Pods/Kingfisher/Sources/ImageProcessor.swift @@ -0,0 +1,397 @@ +// +// ImageProcessor.swift +// Kingfisher +// +// Created by Wei Wang on 2016/08/26. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import CoreGraphics + + +/// The item which could be processed by an `ImageProcessor` +/// +/// - image: Input image +/// - data: Input data +public enum ImageProcessItem { + case image(Image) + case data(Data) +} + +/// An `ImageProcessor` would be used to convert some downloaded data to an image. +public protocol ImageProcessor { + /// Identifier of the processor. It will be used to identify the processor when + /// caching and retriving an image. You might want to make sure that processors with + /// same properties/functionality have the same identifiers, so correct processed images + /// could be retrived with proper key. + /// + /// - Note: Do not supply an empty string for a customized processor, which is already taken by + /// the `DefaultImageProcessor`. It is recommended to use a reverse domain name notation + /// string of your own for the identifier. + var identifier: String { get } + + /// Process an input `ImageProcessItem` item to an image for this processor. + /// + /// - parameter item: Input item which will be processed by `self` + /// - parameter options: Options when processing the item. + /// + /// - returns: The processed image. + /// + /// - Note: The return value will be `nil` if processing failed while converting data to image. + /// If input item is already an image and there is any errors in processing, the input + /// image itself will be returned. + /// - Note: Most processor only supports CG-based images. + /// watchOS is not supported for processers containing filter, the input image will be returned directly on watchOS. + func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? +} + +typealias ProcessorImp = ((ImageProcessItem, KingfisherOptionsInfo) -> Image?) + +public extension ImageProcessor { + + /// Append an `ImageProcessor` to another. The identifier of the new `ImageProcessor` + /// will be "\(self.identifier)|>\(another.identifier)". + /// + /// - parameter another: An `ImageProcessor` you want to append to `self`. + /// + /// - returns: The new `ImageProcessor`. It will process the image in the order + /// of the two processors concatenated. + public func append(another: ImageProcessor) -> ImageProcessor { + let newIdentifier = identifier.appending("|>\(another.identifier)") + return GeneralProcessor(identifier: newIdentifier) { + item, options in + if let image = self.process(item: item, options: options) { + return another.process(item: .image(image), options: options) + } else { + return nil + } + } + } +} + +fileprivate struct GeneralProcessor: ImageProcessor { + let identifier: String + let p: ProcessorImp + func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + return p(item, options) + } +} + +/// The default processor. It convert the input data to a valid image. +/// Images of .PNG, .JPEG and .GIF format are supported. +/// If an image is given, `DefaultImageProcessor` will do nothing on it and just return that image. +public struct DefaultImageProcessor: ImageProcessor { + + /// A default `DefaultImageProcessor` could be used across. + public static let `default` = DefaultImageProcessor() + + public let identifier = "" + + /// Initialize a `DefaultImageProcessor` + /// + /// - returns: An initialized `DefaultImageProcessor`. + public init() {} + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image + case .data(let data): + return Kingfisher.image( + data: data, + scale: options.scaleFactor, + preloadAllGIFData: options.preloadAllGIFData, + onlyFirstFrame: options.onlyLoadFirstFrame) + } + } +} + +/// Processor for making round corner images. Only CG-based images are supported in macOS, +/// if a non-CG image passed in, the processor will do nothing. +public struct RoundCornerImageProcessor: ImageProcessor { + public let identifier: String + + /// Corner radius will be applied in processing. + public let cornerRadius: CGFloat + + /// Target size of output image should be. If `nil`, the image will keep its original size after processing. + public let targetSize: CGSize? + + /// Initialize a `RoundCornerImageProcessor` + /// + /// - parameter cornerRadius: Corner radius will be applied in processing. + /// - parameter targetSize: Target size of output image should be. If `nil`, + /// the image will keep its original size after processing. + /// Default is `nil`. + /// + /// - returns: An initialized `RoundCornerImageProcessor`. + public init(cornerRadius: CGFloat, targetSize: CGSize? = nil) { + self.cornerRadius = cornerRadius + self.targetSize = targetSize + if let size = targetSize { + self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size))" + } else { + self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius))" + } + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + let size = targetSize ?? image.kf.size + return image.kf.image(withRoundRadius: cornerRadius, fit: size) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + + +/// Specify how a size adjusts itself to fit a target. +/// +/// - none: Not scale the content. +/// - aspectFit: Scale the content to fit the size of the view by maintaining the aspect ratio. +/// - aspectFill: Scale the content to fill the size of the view +public enum ContentMode { + case none + case aspectFit + case aspectFill +} + +/// Processor for resizing images. Only CG-based images are supported in macOS. +public struct ResizingImageProcessor: ImageProcessor { + public let identifier: String + + /// Target size of output image should be. + public let targetSize: CGSize + + /// Target content mode of output image should be. + /// Default to ContentMode.none + public let targetContentMode: ContentMode + + /// Initialize a `ResizingImageProcessor` + /// + /// - parameter targetSize: Target size of output image should be. + /// - parameter contentMode: Target content mode of output image should be. + /// + /// - returns: An initialized `ResizingImageProcessor`. + public init(targetSize: CGSize, contentMode: ContentMode = .none) { + self.targetSize = targetSize + self.targetContentMode = contentMode + + if contentMode == .none { + self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(targetSize))" + } else { + self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(targetSize), \(contentMode))" + } + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf.resize(to: targetSize, for: targetContentMode) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + +/// Processor for adding blur effect to images. `Accelerate.framework` is used underhood for +/// a better performance. A simulated Gaussian blur with specified blur radius will be applied. +public struct BlurImageProcessor: ImageProcessor { + public let identifier: String + + /// Blur radius for the simulated Gaussian blur. + public let blurRadius: CGFloat + + /// Initialize a `BlurImageProcessor` + /// + /// - parameter blurRadius: Blur radius for the simulated Gaussian blur. + /// + /// - returns: An initialized `BlurImageProcessor`. + public init(blurRadius: CGFloat) { + self.blurRadius = blurRadius + self.identifier = "com.onevcat.Kingfisher.BlurImageProcessor(\(blurRadius))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + let radius = blurRadius * options.scaleFactor + return image.kf.blurred(withRadius: radius) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + +/// Processor for adding an overlay to images. Only CG-based images are supported in macOS. +public struct OverlayImageProcessor: ImageProcessor { + + public var identifier: String + + /// Overlay color will be used to overlay the input image. + public let overlay: Color + + /// Fraction will be used when overlay the color to image. + public let fraction: CGFloat + + /// Initialize an `OverlayImageProcessor` + /// + /// - parameter overlay: Overlay color will be used to overlay the input image. + /// - parameter fraction: Fraction will be used when overlay the color to image. + /// From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay. + /// + /// - returns: An initialized `OverlayImageProcessor`. + public init(overlay: Color, fraction: CGFloat = 0.5) { + self.overlay = overlay + self.fraction = fraction + self.identifier = "com.onevcat.Kingfisher.OverlayImageProcessor(\(overlay.hex)_\(fraction))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf.overlaying(with: overlay, fraction: fraction) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + +/// Processor for tint images with color. Only CG-based images are supported. +public struct TintImageProcessor: ImageProcessor { + + public let identifier: String + + /// Tint color will be used to tint the input image. + public let tint: Color + + /// Initialize a `TintImageProcessor` + /// + /// - parameter tint: Tint color will be used to tint the input image. + /// + /// - returns: An initialized `TintImageProcessor`. + public init(tint: Color) { + self.tint = tint + self.identifier = "com.onevcat.Kingfisher.TintImageProcessor(\(tint.hex))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf.tinted(with: tint) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + +/// Processor for applying some color control to images. Only CG-based images are supported. +/// watchOS is not supported. +public struct ColorControlsProcessor: ImageProcessor { + + public let identifier: String + + /// Brightness changing to image. + public let brightness: CGFloat + + /// Contrast changing to image. + public let contrast: CGFloat + + /// Saturation changing to image. + public let saturation: CGFloat + + /// InputEV changing to image. + public let inputEV: CGFloat + + /// Initialize a `ColorControlsProcessor` + /// + /// - parameter brightness: Brightness changing to image. + /// - parameter contrast: Contrast changing to image. + /// - parameter saturation: Saturation changing to image. + /// - parameter inputEV: InputEV changing to image. + /// + /// - returns: An initialized `ColorControlsProcessor` + public init(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) { + self.brightness = brightness + self.contrast = contrast + self.saturation = saturation + self.inputEV = inputEV + self.identifier = "com.onevcat.Kingfisher.ColorControlsProcessor(\(brightness)_\(contrast)_\(saturation)_\(inputEV))" + } + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + switch item { + case .image(let image): + return image.kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV) + case .data(_): + return (DefaultImageProcessor.default >> self).process(item: item, options: options) + } + } +} + +/// Processor for applying black and white effect to images. Only CG-based images are supported. +/// watchOS is not supported. +public struct BlackWhiteProcessor: ImageProcessor { + public let identifier = "com.onevcat.Kingfisher.BlackWhiteProcessor" + + /// Initialize a `BlackWhiteProcessor` + /// + /// - returns: An initialized `BlackWhiteProcessor` + public init() {} + + public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { + return ColorControlsProcessor(brightness: 0.0, contrast: 1.0, saturation: 0.0, inputEV: 0.7) + .process(item: item, options: options) + } +} + +/// Concatenate two `ImageProcessor`s. `ImageProcessor.appen(another:)` is used internally. +/// +/// - parameter left: First processor. +/// - parameter right: Second processor. +/// +/// - returns: The concatenated processor. +public func >>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor { + return left.append(another: right) +} + +fileprivate extension Color { + var hex: String { + var r: CGFloat = 0 + var g: CGFloat = 0 + var b: CGFloat = 0 + var a: CGFloat = 0 + + getRed(&r, green: &g, blue: &b, alpha: &a) + + let rInt = Int(r * 255) << 24 + let gInt = Int(g * 255) << 16 + let bInt = Int(b * 255) << 8 + let aInt = Int(a * 255) + + let rgba = rInt | gInt | bInt | aInt + + return String(format:"#%08x", rgba) + } +} diff --git a/Pods/Kingfisher/Sources/ImageTransition.swift b/Pods/Kingfisher/Sources/ImageTransition.swift new file mode 100755 index 0000000..8ce42e8 --- /dev/null +++ b/Pods/Kingfisher/Sources/ImageTransition.swift @@ -0,0 +1,128 @@ +// +// ImageTransition.swift +// Kingfisher +// +// Created by Wei Wang on 15/9/18. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +// Not implemented for macOS and watchOS yet. + +import AppKit + +/// Image transition is not supported on macOS. +public enum ImageTransition { + case none + var duration: TimeInterval { + return 0 + } +} + +#elseif os(watchOS) +import UIKit +/// Image transition is not supported on watchOS. +public enum ImageTransition { + case none + var duration: TimeInterval { + return 0 + } +} +#else +import UIKit + +/** +Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. +You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` +to enable the animation transition. + +Apple's UIViewAnimationOptions is used under the hood. +For custom transition, you should specified your own transition options, animations and +comletion handler as well. +*/ +public enum ImageTransition { + /// No animation transistion. + case none + + /// Fade in the loaded image. + case fade(TimeInterval) + + /// Flip from left transition. + case flipFromLeft(TimeInterval) + + /// Flip from right transition. + case flipFromRight(TimeInterval) + + /// Flip from top transition. + case flipFromTop(TimeInterval) + + /// Flip from bottom transition. + case flipFromBottom(TimeInterval) + + /// Custom transition. + case custom(duration: TimeInterval, + options: UIViewAnimationOptions, + animations: ((UIImageView, UIImage) -> Void)?, + completion: ((Bool) -> Void)?) + + var duration: TimeInterval { + switch self { + case .none: return 0 + case .fade(let duration): return duration + + case .flipFromLeft(let duration): return duration + case .flipFromRight(let duration): return duration + case .flipFromTop(let duration): return duration + case .flipFromBottom(let duration): return duration + + case .custom(let duration, _, _, _): return duration + } + } + + var animationOptions: UIViewAnimationOptions { + switch self { + case .none: return [] + case .fade(_): return .transitionCrossDissolve + + case .flipFromLeft(_): return .transitionFlipFromLeft + case .flipFromRight(_): return .transitionFlipFromRight + case .flipFromTop(_): return .transitionFlipFromTop + case .flipFromBottom(_): return .transitionFlipFromBottom + + case .custom(_, let options, _, _): return options + } + } + + var animations: ((UIImageView, UIImage) -> Void)? { + switch self { + case .custom(_, _, let animations, _): return animations + default: return { $0.image = $1 } + } + } + + var completion: ((Bool) -> Void)? { + switch self { + case .custom(_, _, _, let completion): return completion + default: return nil + } + } +} +#endif diff --git a/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift b/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift new file mode 100755 index 0000000..ac92a4f --- /dev/null +++ b/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift @@ -0,0 +1,292 @@ +// +// ImageView+Kingfisher.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +// MARK: - Extension methods. +/** + * Set image to use from web. + */ +extension Kingfisher where Base: ImageView { + /** + Set an image with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + public func setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + guard let resource = resource else { + base.image = placeholder + setWebURL(nil) + completionHandler?(nil, nil, .none, nil) + return .empty + } + + var options = options ?? KingfisherEmptyOptionsInfo + + if !options.keepCurrentImageWhileLoading { + base.image = placeholder + } + + let maybeIndicator = indicator + maybeIndicator?.startAnimatingView() + + setWebURL(resource.downloadURL) + + if base.shouldPreloadAllGIF() { + options.append(.preloadAllGIFData) + } + + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, + progressBlock: { receivedSize, totalSize in + guard resource.downloadURL == self.webURL else { + return + } + if let progressBlock = progressBlock { + progressBlock(receivedSize, totalSize) + } + }, + completionHandler: {[weak base] image, error, cacheType, imageURL in + DispatchQueue.main.safeAsync { + guard let strongBase = base, imageURL == self.webURL else { + return + } + self.setImageTask(nil) + guard let image = image else { + maybeIndicator?.stopAnimatingView() + completionHandler?(nil, error, cacheType, imageURL) + return + } + + guard let transitionItem = options.firstMatchIgnoringAssociatedValue(.transition(.none)), + case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else + { + maybeIndicator?.stopAnimatingView() + strongBase.image = image + completionHandler?(image, error, cacheType, imageURL) + return + } + + #if !os(macOS) + UIView.transition(with: strongBase, duration: 0.0, options: [], + animations: { maybeIndicator?.stopAnimatingView() }, + completion: { _ in + UIView.transition(with: strongBase, duration: transition.duration, + options: [transition.animationOptions, .allowUserInteraction], + animations: { + // Set image property in the animation. + transition.animations?(strongBase, image) + }, + completion: { finished in + transition.completion?(finished) + completionHandler?(image, error, cacheType, imageURL) + }) + }) + #endif + } + }) + + setImageTask(task) + + return task + } + + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func cancelDownloadTask() { + imageTask?.cancel() + } +} + +// MARK: - Associated Object +private var lastURLKey: Void? +private var indicatorKey: Void? +private var indicatorTypeKey: Void? +private var imageTaskKey: Void? + +extension Kingfisher where Base: ImageView { + /// Get the image URL binded to this image view. + public var webURL: URL? { + return objc_getAssociatedObject(base, &lastURLKey) as? URL + } + + fileprivate func setWebURL(_ url: URL?) { + objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + /// Holds which indicator type is going to be used. + /// Default is .none, means no indicator will be shown. + public var indicatorType: IndicatorType { + get { + let indicator = (objc_getAssociatedObject(base, &indicatorTypeKey) as? Box)?.value + return indicator ?? .none + } + + set { + switch newValue { + case .none: + indicator = nil + case .activity: + indicator = ActivityIndicator() + case .image(let data): + indicator = ImageIndicator(imageData: data) + case .custom(let anIndicator): + indicator = anIndicator + } + + objc_setAssociatedObject(base, &indicatorTypeKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + /// Holds any type that conforms to the protocol `Indicator`. + /// The protocol `Indicator` has a `view` property that will be shown when loading an image. + /// It will be `nil` if `indicatorType` is `.none`. + public fileprivate(set) var indicator: Indicator? { + get { + return (objc_getAssociatedObject(base, &indicatorKey) as? Box)?.value + } + + set { + // Remove previous + if let previousIndicator = indicator { + previousIndicator.view.removeFromSuperview() + } + + // Add new + if var newIndicator = newValue { + newIndicator.view.frame = base.frame + newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY) + newIndicator.view.isHidden = true + base.addSubview(newIndicator.view) + } + + // Save in associated object + objc_setAssociatedObject(base, &indicatorKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + fileprivate var imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask + } + + fileprivate func setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + + +// MARK: - Deprecated. Only for back compatibility. +/** +* Set image to use from web. Deprecated. Use `kf` namespacing instead. +*/ +extension ImageView { + /** + Set an image with a resource, a placeholder image, options, progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.setImage` instead.", renamed: "kf.setImage") + @discardableResult + public func kf_setImage(with resource: Resource?, + placeholder: Image? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setImage(with: resource, placeholder: placeholder, options: options, progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.cancelDownloadTask` instead.", renamed: "kf.cancelDownloadTask") + public func kf_cancelDownloadTask() { kf.cancelDownloadTask() } + + /// Get the image URL binded to this image view. + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.webURL` instead.", renamed: "kf.webURL") + public var kf_webURL: URL? { return kf.webURL } + + /// Holds which indicator type is going to be used. + /// Default is .none, means no indicator will be shown. + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.indicatorType` instead.", renamed: "kf.indicatorType") + public var kf_indicatorType: IndicatorType { + get { return kf.indicatorType } + set { kf.indicatorType = newValue } + } + + @available(*, deprecated, message: "Extensions directly on image views are deprecated. Use `imageView.kf.indicator` instead.", renamed: "kf.indicator") + /// Holds any type that conforms to the protocol `Indicator`. + /// The protocol `Indicator` has a `view` property that will be shown when loading an image. + /// It will be `nil` if `kf_indicatorType` is `.none`. + public private(set) var kf_indicator: Indicator? { + get { return kf.indicator } + set { kf.indicator = newValue } + } + + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.imageTask") + fileprivate var kf_imageTask: RetrieveImageTask? { return kf.imageTask } + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.setImageTask") + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task) } + @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.setWebURL") + fileprivate func kf_setWebURL(_ url: URL) { kf.setWebURL(url) } +} + +extension ImageView { + func shouldPreloadAllGIF() -> Bool { return true } +} diff --git a/Pods/Kingfisher/Sources/Indicator.swift b/Pods/Kingfisher/Sources/Indicator.swift new file mode 100644 index 0000000..d1f55f0 --- /dev/null +++ b/Pods/Kingfisher/Sources/Indicator.swift @@ -0,0 +1,191 @@ +// +// Indicator.swift +// Kingfisher +// +// Created by João D. Moreira on 30/08/16. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) + import AppKit +#else + import UIKit +#endif + +#if os(macOS) + public typealias IndicatorView = NSView +#else + public typealias IndicatorView = UIView +#endif + +public enum IndicatorType { + /// No indicator. + case none + /// Use system activity indicator. + case activity + /// Use an image as indicator. GIF is supported. + case image(imageData: Data) + /// Use a custom indicator, which conforms to the `Indicator` protocol. + case custom(indicator: Indicator) +} + +// MARK: - Indicator Protocol +public protocol Indicator { + func startAnimatingView() + func stopAnimatingView() + + var viewCenter: CGPoint { get set } + var view: IndicatorView { get } +} + +extension Indicator { + #if os(macOS) + public var viewCenter: CGPoint { + get { + let frame = view.frame + return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 ) + } + set { + let frame = view.frame + let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, + y: newValue.y - frame.size.height / 2.0, + width: frame.size.width, + height: frame.size.height) + view.frame = newFrame + } + } + #else + public var viewCenter: CGPoint { + get { + return view.center + } + set { + view.center = newValue + } + } + #endif +} + +// MARK: - ActivityIndicator +// Displays a NSProgressIndicator / UIActivityIndicatorView +struct ActivityIndicator: Indicator { + + #if os(macOS) + private let activityIndicatorView: NSProgressIndicator + #else + private let activityIndicatorView: UIActivityIndicatorView + #endif + + var view: IndicatorView { + return activityIndicatorView + } + + func startAnimatingView() { + #if os(macOS) + activityIndicatorView.startAnimation(nil) + #else + activityIndicatorView.startAnimating() + #endif + activityIndicatorView.isHidden = false + } + + func stopAnimatingView() { + #if os(macOS) + activityIndicatorView.stopAnimation(nil) + #else + activityIndicatorView.stopAnimating() + #endif + activityIndicatorView.isHidden = true + } + + init() { + #if os(macOS) + activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) + activityIndicatorView.controlSize = .small + activityIndicatorView.style = .spinningStyle + #else + #if os(tvOS) + let indicatorStyle = UIActivityIndicatorViewStyle.white + #else + let indicatorStyle = UIActivityIndicatorViewStyle.gray + #endif + activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle) + activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin] + #endif + } +} + +// MARK: - ImageIndicator +// Displays an ImageView. Supports gif +struct ImageIndicator: Indicator { + private let animatedImageIndicatorView: ImageView + + var view: IndicatorView { + return animatedImageIndicatorView + } + + init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) { + + var options = options + // Use normal image view to show gif, so we need to preload all gif data. + if !options.preloadAllGIFData { + options.append(.preloadAllGIFData) + } + + guard let image = processor.process(item: .data(data), options: options) else { + return nil + } + + animatedImageIndicatorView = ImageView() + animatedImageIndicatorView.image = image + + #if os(macOS) + // Need for gif to animate on macOS + self.animatedImageIndicatorView.imageScaling = .scaleNone + self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true + #else + animatedImageIndicatorView.contentMode = .center + + animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin, + .flexibleRightMargin, + .flexibleBottomMargin, + .flexibleTopMargin] + #endif + } + + func startAnimatingView() { + #if os(macOS) + animatedImageIndicatorView.animates = true + #else + animatedImageIndicatorView.startAnimating() + #endif + animatedImageIndicatorView.isHidden = false + } + + func stopAnimatingView() { + #if os(macOS) + animatedImageIndicatorView.animates = false + #else + animatedImageIndicatorView.stopAnimating() + #endif + animatedImageIndicatorView.isHidden = true + } +} diff --git a/Pods/Kingfisher/Sources/Kingfisher.h b/Pods/Kingfisher/Sources/Kingfisher.h new file mode 100644 index 0000000..aed591d --- /dev/null +++ b/Pods/Kingfisher/Sources/Kingfisher.h @@ -0,0 +1,37 @@ +// +// Kingfisher.h +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +//! Project version number for Kingfisher. +FOUNDATION_EXPORT double KingfisherVersionNumber; + +//! Project version string for Kingfisher. +FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Pods/Kingfisher/Sources/Kingfisher.swift b/Pods/Kingfisher/Sources/Kingfisher.swift new file mode 100644 index 0000000..6e2d409 --- /dev/null +++ b/Pods/Kingfisher/Sources/Kingfisher.swift @@ -0,0 +1,71 @@ +// +// Kingfisher.swift +// Kingfisher +// +// Created by Wei Wang on 16/9/14. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import ImageIO + +#if os(macOS) + import AppKit + public typealias Image = NSImage + public typealias Color = NSColor + public typealias ImageView = NSImageView + typealias Button = NSButton +#else + import UIKit + public typealias Image = UIImage + public typealias Color = UIColor + #if !os(watchOS) + public typealias ImageView = UIImageView + typealias Button = UIButton + #endif +#endif + +public final class Kingfisher { + public let base: Base + public init(_ base: Base) { + self.base = base + } +} + +/** + A type that has Kingfisher extensions. + */ +public protocol KingfisherCompatible { + associatedtype CompatibleType + var kf: CompatibleType { get } +} + +public extension KingfisherCompatible { + public var kf: Kingfisher { + get { return Kingfisher(self) } + } +} + +extension Image: KingfisherCompatible { } +#if !os(watchOS) +extension ImageView: KingfisherCompatible { } +extension Button: KingfisherCompatible { } +#endif diff --git a/Pods/Kingfisher/Sources/KingfisherManager.swift b/Pods/Kingfisher/Sources/KingfisherManager.swift new file mode 100755 index 0000000..eb29f3d --- /dev/null +++ b/Pods/Kingfisher/Sources/KingfisherManager.swift @@ -0,0 +1,205 @@ +// +// KingfisherManager.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> ()) +public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> ()) + +/// RetrieveImageTask represents a task of image retrieving process. +/// It contains an async task of getting image from disk and from network. +public class RetrieveImageTask { + + public static let empty = RetrieveImageTask() + + // If task is canceled before the download task started (which means the `downloadTask` is nil), + // the download task should not begin. + var cancelledBeforeDownloadStarting: Bool = false + + /// The disk retrieve task in this image task. Kingfisher will try to look up in cache first. This task represent the cache search task. + @available(*, deprecated, + message: "diskRetrieveTask is not in use anymore. You cannot cancel a disk retrieve task anymore once it started.") + public var diskRetrieveTask: RetrieveImageDiskTask? + + /// The network retrieve task in this image task. + public var downloadTask: RetrieveImageDownloadTask? + + /** + Cancel current task. If this task is already done, do nothing. + */ + public func cancel() { + if let downloadTask = downloadTask { + downloadTask.cancel() + } else { + cancelledBeforeDownloadStarting = true + } + } +} + +/// Error domain of Kingfisher +public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error" + +/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache. +/// You can use this class to retrieve an image via a specified URL from web or cache. +public class KingfisherManager { + + /// Shared manager used by the extensions across Kingfisher. + public static let shared = KingfisherManager() + + /// Cache used by this manager + public var cache: ImageCache + + /// Downloader used by this manager + public var downloader: ImageDownloader + + convenience init() { + self.init(downloader: .default, cache: .default) + } + + init(downloader: ImageDownloader, cache: ImageCache) { + self.downloader = downloader + self.cache = cache + } + + /** + Get an image with resource. + If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. + If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. + These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI. + - parameter completionHandler: Called when the whole retrieving process finished. + + - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. + */ + @discardableResult + public func retrieveImage(with resource: Resource, + options: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + let task = RetrieveImageTask() + + if let options = options, options.forceRefresh { + _ = downloadAndCacheImage( + with: resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: completionHandler, + options: options) + } else { + tryToRetrieveImageFromCache( + forKey: resource.cacheKey, + with: resource.downloadURL, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: completionHandler, + options: options) + } + + return task + } + + @discardableResult + func downloadAndCacheImage(with url: URL, + forKey key: String, + retrieveImageTask: RetrieveImageTask, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?, + options: KingfisherOptionsInfo?) -> RetrieveImageDownloadTask? + { + let options = options ?? KingfisherEmptyOptionsInfo + let downloader = options.downloader + return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options, + progressBlock: { receivedSize, totalSize in + progressBlock?(receivedSize, totalSize) + }, + completionHandler: { image, error, imageURL, originalData in + + let targetCache = options.targetCache + if let error = error, error.code == KingfisherError.notModified.rawValue { + // Not modified. Try to find the image from cache. + // (The image should be in cache. It should be guaranteed by the framework users.) + targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> () in + completionHandler?(cacheImage, nil, cacheType, url) + }) + return + } + + if let image = image, let originalData = originalData { + targetCache.store(image, + original: originalData, + forKey: key, + processorIdentifier:options.processor.identifier, + cacheSerializer: options.cacheSerializer, + toDisk: !options.cacheMemoryOnly, + completionHandler: nil) + } + + completionHandler?(image, error, .none, url) + + }) + } + + func tryToRetrieveImageFromCache(forKey key: String, + with url: URL, + retrieveImageTask: RetrieveImageTask, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?, + options: KingfisherOptionsInfo?) + { + let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in + completionHandler?(image, error, cacheType, imageURL) + } + + let targetCache = options?.targetCache ?? cache + targetCache.retrieveImage(forKey: key, options: options, + completionHandler: { image, cacheType in + if image != nil { + diskTaskCompletionHandler(image, nil, cacheType, url) + } else if let options = options, options.onlyFromCache { + let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil) + diskTaskCompletionHandler(nil, error, .none, url) + } else { + self.downloadAndCacheImage( + with: url, + forKey: key, + retrieveImageTask: retrieveImageTask, + progressBlock: progressBlock, + completionHandler: diskTaskCompletionHandler, + options: options) + } + } + ) + } +} diff --git a/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift b/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift new file mode 100755 index 0000000..948906e --- /dev/null +++ b/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift @@ -0,0 +1,296 @@ +// +// KingfisherOptionsInfo.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/23. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + + +/** +* KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher. +*/ +public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem] +let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]() + +/** +Items could be added into KingfisherOptionsInfo. +*/ +public enum KingfisherOptionsInfoItem { + /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified + /// cache object when handling related operations, including trying to retrieve the cached images and store + /// the downloaded image to it. + case targetCache(ImageCache) + + /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this + /// downloader to download the images. + case downloader(ImageDownloader) + + /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of + /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the + /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when + /// the image being retrieved from cache, set `ForceTransition` as well. + case transition(ImageTransition) + + /// Associated `Float` value will be set as the priority of image download task. The value for it should be + /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used. + case downloadPriority(Float) + + /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource. + case forceRefresh + + /// If set, setting the image to an image view will happen with transition even when retrieved from cache. + /// See `Transition` option for more. + case forceTransition + + /// If set, `Kingfisher` will only cache the value in memory but not in disk. + case cacheMemoryOnly + + /// If set, `Kingfisher` will only try to retrieve the image from cache not from network. + case onlyFromCache + + /// Decode the image in background thread before using. + case backgroundDecode + + /// The associated value of this member will be used as the target queue of dispatch callbacks when + /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks. + case callbackDispatchQueue(DispatchQueue?) + + /// The associated value of this member will be used as the scale factor when converting retrieved data to an image. + /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing + /// with 2x or 3x retina images. + case scaleFactor(CGFloat) + + /// Whether all the GIF data should be preloaded. Default it false, which means following frames will be + /// loaded on need. If true, all the GIF data will be loaded and decoded into memory. This option is mainly + /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload + /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use + /// corresponding image view type instead of setting this option. + case preloadAllGIFData + + /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent. + /// This is the last chance you can modify the request. You can modify the request for some customizing purpose, + /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request + /// will be sent without any modification by default. + case requestModifier(ImageDownloadRequestModifier) + + /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image + /// and/or apply some filter on it. If a cache is connected to the downloader (it happenes when you are using + /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the + /// image view. `DefaultImageProcessor.default` will be used by default. + case processor(ImageProcessor) + + /// Supply an `CacheSerializer` to convert some data to an image object for + /// retrieving from disk cache or vice versa for storing to disk cache. + /// `DefaultCacheSerializer.default` will be used by default. + case cacheSerializer(CacheSerializer) + + /// Keep the existing image while setting another image to an image view. + /// By setting this option, the placeholder image parameter of imageview extension method + /// will be ignored and the current image will be kept while loading or downloading the new image. + case keepCurrentImageWhileLoading + + /// If set, Kingfisher will only load the first frame from a GIF file as a single image. + /// Loading a lot of GIFs may take too much memory. It will be useful when you want to display a + /// static preview of the first frame from a GIF image. + /// This option will be ignored if the target image is not GIF. + case onlyLoadFirstFrame +} + +precedencegroup ItemComparisonPrecedence { + associativity: none + higherThan: LogicalConjunctionPrecedence +} + +infix operator <== : ItemComparisonPrecedence + +// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values. +func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool { + switch (lhs, rhs) { + case (.targetCache(_), .targetCache(_)): return true + case (.downloader(_), .downloader(_)): return true + case (.transition(_), .transition(_)): return true + case (.downloadPriority(_), .downloadPriority(_)): return true + case (.forceRefresh, .forceRefresh): return true + case (.forceTransition, .forceTransition): return true + case (.cacheMemoryOnly, .cacheMemoryOnly): return true + case (.onlyFromCache, .onlyFromCache): return true + case (.backgroundDecode, .backgroundDecode): return true + case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true + case (.scaleFactor(_), .scaleFactor(_)): return true + case (.preloadAllGIFData, .preloadAllGIFData): return true + case (.requestModifier(_), .requestModifier(_)): return true + case (.processor(_), .processor(_)): return true + case (.cacheSerializer(_), .cacheSerializer(_)): return true + case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true + case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true + default: return false + } +} + +extension Collection where Iterator.Element == KingfisherOptionsInfoItem { + func firstMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? { + return index { $0 <== target }.flatMap { self[$0] } + } + + func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { + return self.filter { !($0 <== target) } + } +} + +public extension Collection where Iterator.Element == KingfisherOptionsInfoItem { + /// The target `ImageCache` which is used. + public var targetCache: ImageCache { + if let item = firstMatchIgnoringAssociatedValue(.targetCache(.default)), + case .targetCache(let cache) = item + { + return cache + } + return ImageCache.default + } + + /// The `ImageDownloader` which is specified. + public var downloader: ImageDownloader { + if let item = firstMatchIgnoringAssociatedValue(.downloader(.default)), + case .downloader(let downloader) = item + { + return downloader + } + return ImageDownloader.default + } + + /// Member for animation transition when using UIImageView. + public var transition: ImageTransition { + if let item = firstMatchIgnoringAssociatedValue(.transition(.none)), + case .transition(let transition) = item + { + return transition + } + return ImageTransition.none + } + + /// A `Float` value set as the priority of image download task. The value for it should be + /// between 0.0~1.0. + public var downloadPriority: Float { + if let item = firstMatchIgnoringAssociatedValue(.downloadPriority(0)), + case .downloadPriority(let priority) = item + { + return priority + } + return URLSessionTask.defaultPriority + } + + /// Whether an image will be always downloaded again or not. + public var forceRefresh: Bool { + return contains{ $0 <== .forceRefresh } + } + + /// Whether the transition should always happen or not. + public var forceTransition: Bool { + return contains{ $0 <== .forceTransition } + } + + /// Whether cache the image only in memory or not. + public var cacheMemoryOnly: Bool { + return contains{ $0 <== .cacheMemoryOnly } + } + + /// Whether only load the images from cache or not. + public var onlyFromCache: Bool { + return contains{ $0 <== .onlyFromCache } + } + + /// Whether the image should be decoded in background or not. + public var backgroundDecode: Bool { + return contains{ $0 <== .backgroundDecode } + } + + /// Whether the image data should be all loaded at once if it is a GIF. + public var preloadAllGIFData: Bool { + return contains { $0 <== .preloadAllGIFData } + } + + /// The queue of callbacks should happen from Kingfisher. + public var callbackDispatchQueue: DispatchQueue { + if let item = firstMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)), + case .callbackDispatchQueue(let queue) = item + { + return queue ?? DispatchQueue.main + } + return DispatchQueue.main + } + + /// The scale factor which should be used for the image. + public var scaleFactor: CGFloat { + if let item = firstMatchIgnoringAssociatedValue(.scaleFactor(0)), + case .scaleFactor(let scale) = item + { + return scale + } + return 1.0 + } + + /// The `ImageDownloadRequestModifier` will be used before sending a download request. + public var modifier: ImageDownloadRequestModifier { + if let item = firstMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)), + case .requestModifier(let modifier) = item + { + return modifier + } + return NoModifier.default + } + + /// `ImageProcessor` for processing when the downloading finishes. + public var processor: ImageProcessor { + if let item = firstMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)), + case .processor(let processor) = item + { + return processor + } + return DefaultImageProcessor.default + } + + /// `CacheSerializer` to convert image to data for storing in cache. + public var cacheSerializer: CacheSerializer { + if let item = firstMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)), + case .cacheSerializer(let cacheSerializer) = item + { + return cacheSerializer + } + return DefaultCacheSerializer.default + } + + /// Keep the existing image while setting another image to an image view. + /// Or the placeholder will be used while downloading. + public var keepCurrentImageWhileLoading: Bool { + return contains { $0 <== .keepCurrentImageWhileLoading } + } + + public var onlyLoadFirstFrame: Bool { + return contains { $0 <== .onlyLoadFirstFrame } + } +} diff --git a/Pods/Kingfisher/Sources/RequestModifier.swift b/Pods/Kingfisher/Sources/RequestModifier.swift new file mode 100644 index 0000000..13d655a --- /dev/null +++ b/Pods/Kingfisher/Sources/RequestModifier.swift @@ -0,0 +1,53 @@ +// +// RequestModifier.swift +// Kingfisher +// +// Created by Wei Wang on 2016/09/05. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +/// Request modifier of image downloader. +public protocol ImageDownloadRequestModifier { + func modified(for request: URLRequest) -> URLRequest? +} + +struct NoModifier: ImageDownloadRequestModifier { + static let `default` = NoModifier() + private init() {} + func modified(for request: URLRequest) -> URLRequest? { + return request + } +} + +public struct AnyModifier: ImageDownloadRequestModifier { + + let block: (URLRequest) -> URLRequest? + + public func modified(for request: URLRequest) -> URLRequest? { + return block(request) + } + + public init(modify: @escaping (URLRequest) -> URLRequest? ) { + block = modify + } +} diff --git a/Pods/Kingfisher/Sources/Resource.swift b/Pods/Kingfisher/Sources/Resource.swift new file mode 100755 index 0000000..c95691a --- /dev/null +++ b/Pods/Kingfisher/Sources/Resource.swift @@ -0,0 +1,74 @@ +// +// Resource.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + + +/// `Resource` protocol defines how to download and cache a resource from network. +public protocol Resource { + /// The key used in cache. + var cacheKey: String { get } + + /// The target image URL. + var downloadURL: URL { get } +} + +/** + ImageResource is a simple combination of `downloadURL` and `cacheKey`. + + When passed to image view set methods, Kingfisher will try to download the target + image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. + */ +public struct ImageResource: Resource { + /// The key used in cache. + public let cacheKey: String + + /// The target image URL. + public let downloadURL: URL + + /** + Create a resource. + + - parameter downloadURL: The target image URL. + - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. + + - returns: A resource. + */ + public init(downloadURL: URL, cacheKey: String? = nil) { + self.downloadURL = downloadURL + self.cacheKey = cacheKey ?? downloadURL.absoluteString + } +} + +/** + URL conforms to `Resource` in Kingfisher. + The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. + If you need customize the url and/or cache key, use `ImageResource` instead. + */ +extension URL: Resource { + public var cacheKey: String { return absoluteString } + public var downloadURL: URL { return self } +} diff --git a/Pods/Kingfisher/Sources/String+MD5.swift b/Pods/Kingfisher/Sources/String+MD5.swift new file mode 100755 index 0000000..05cf18d --- /dev/null +++ b/Pods/Kingfisher/Sources/String+MD5.swift @@ -0,0 +1,292 @@ +// +// String+MD5.swift +// Kingfisher +// +// To date, adding CommonCrypto to a Swift framework is problematic. See: +// http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework +// We're using a subset and modified version of CryptoSwift as an alternative. +// The following is an altered source version that only includes MD5. The original software can be found at: +// https://github.com/krzyzanowskim/CryptoSwift +// This is the original copyright notice: + +/* +Copyright (C) 2014 Marcin Krzyżanowski +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +- This notice may not be removed or altered from any source or binary distribution. +*/ + +import Foundation + +public struct StringProxy { + fileprivate let base: String + init(proxy: String) { + base = proxy + } +} + +extension String: KingfisherCompatible { + public typealias CompatibleType = StringProxy + public var kf: CompatibleType { + return StringProxy(proxy: self) + } +} + +extension StringProxy { + var md5: String { + if let data = base.data(using: .utf8, allowLossyConversion: true) { + + let message = data.withUnsafeBytes { bytes -> [UInt8] in + return Array(UnsafeBufferPointer(start: bytes, count: data.count)) + } + + let MD5Calculator = MD5(message) + let MD5Data = MD5Calculator.calculate() + + let MD5String = NSMutableString() + for c in MD5Data { + MD5String.appendFormat("%02x", c) + } + return MD5String as String + + } else { + return base + } + } +} + + +/** array of bytes, little-endian representation */ +func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] { + let totalBytes = length ?? (MemoryLayout.size * 8) + + let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) + valuePointer.pointee = value + + let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in + var bytes = [UInt8](repeating: 0, count: totalBytes) + for j in 0...size, totalBytes) { + bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee + } + return bytes + } + + valuePointer.deinitialize() + valuePointer.deallocate(capacity: 1) + + return bytes +} + +extension Int { + /** Array of bytes with optional padding (little-endian) */ + func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] { + return arrayOfBytes(self, length: totalBytes) + } + +} + +extension NSMutableData { + + /** Convenient way to append bytes */ + func appendBytes(_ arrayOfBytes: [UInt8]) { + append(arrayOfBytes, length: arrayOfBytes.count) + } + +} + +protocol HashProtocol { + var message: Array { get } + + /** Common part for hash calculation. Prepare header data. */ + func prepare(_ len: Int) -> Array +} + +extension HashProtocol { + + func prepare(_ len: Int) -> Array { + var tmpMessage = message + + // Step 1. Append Padding Bits + tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message + + // append "0" bit until message length in bits ≡ 448 (mod 512) + var msgLength = tmpMessage.count + var counter = 0 + + while msgLength % len != (len - 8) { + counter += 1 + msgLength += 1 + } + + tmpMessage += Array(repeating: 0, count: counter) + return tmpMessage + } +} + +func toUInt32Array(_ slice: ArraySlice) -> Array { + var result = Array() + result.reserveCapacity(16) + + for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout.size) { + let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24 + let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16 + let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8 + let d3 = UInt32(slice[idx]) + let val: UInt32 = d0 | d1 | d2 | d3 + + result.append(val) + } + return result +} + +struct BytesIterator: IteratorProtocol { + + let chunkSize: Int + let data: [UInt8] + + init(chunkSize: Int, data: [UInt8]) { + self.chunkSize = chunkSize + self.data = data + } + + var offset = 0 + + mutating func next() -> ArraySlice? { + let end = min(chunkSize, data.count - offset) + let result = data[offset.. 0 ? result : nil + } +} + +struct BytesSequence: Sequence { + let chunkSize: Int + let data: [UInt8] + + func makeIterator() -> BytesIterator { + return BytesIterator(chunkSize: chunkSize, data: data) + } +} + +func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 { + return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits)) +} + +class MD5: HashProtocol { + + static let size = 16 // 128 / 8 + let message: [UInt8] + + init (_ message: [UInt8]) { + self.message = message + } + + /** specifies the per-round shift amounts */ + private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21] + + /** binary integer part of the sines of integers (Radians) */ + private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391] + + private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] + + func calculate() -> [UInt8] { + var tmpMessage = prepare(64) + tmpMessage.reserveCapacity(tmpMessage.count + 4) + + // hash values + var hh = hashes + + // Step 2. Append Length a 64-bit representation of lengthInBits + let lengthInBits = (message.count * 8) + let lengthBytes = lengthInBits.bytes(64 / 8) + tmpMessage += lengthBytes.reversed() + + // Process the message in successive 512-bit chunks: + let chunkSizeBytes = 512 / 8 // 64 + + for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) { + // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 + var M = toUInt32Array(chunk) + assert(M.count == 16, "Invalid array") + + // Initialize hash value for this chunk: + var A: UInt32 = hh[0] + var B: UInt32 = hh[1] + var C: UInt32 = hh[2] + var D: UInt32 = hh[3] + + var dTemp: UInt32 = 0 + + // Main loop + for j in 0 ..< sines.count { + var g = 0 + var F: UInt32 = 0 + + switch j { + case 0...15: + F = (B & C) | ((~B) & D) + g = j + break + case 16...31: + F = (D & B) | (~D & C) + g = (5 * j + 1) % 16 + break + case 32...47: + F = B ^ C ^ D + g = (3 * j + 5) % 16 + break + case 48...63: + F = C ^ (B | (~D)) + g = (7 * j) % 16 + break + default: + break + } + dTemp = D + D = C + C = B + B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j]) + A = dTemp + } + + hh[0] = hh[0] &+ A + hh[1] = hh[1] &+ B + hh[2] = hh[2] &+ C + hh[3] = hh[3] &+ D + } + + var result = [UInt8]() + result.reserveCapacity(hh.count / 4) + + hh.forEach { + let itemLE = $0.littleEndian + let r1 = UInt8(itemLE & 0xff) + let r2 = UInt8((itemLE >> 8) & 0xff) + let r3 = UInt8((itemLE >> 16) & 0xff) + let r4 = UInt8((itemLE >> 24) & 0xff) + result += [r1, r2, r3, r4] + } + return result + } +} diff --git a/Pods/Kingfisher/Sources/ThreadHelper.swift b/Pods/Kingfisher/Sources/ThreadHelper.swift new file mode 100755 index 0000000..f0742cd --- /dev/null +++ b/Pods/Kingfisher/Sources/ThreadHelper.swift @@ -0,0 +1,40 @@ +// +// ThreadHelper.swift +// Kingfisher +// +// Created by Wei Wang on 15/10/9. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +extension DispatchQueue { + // This method will dispatch the `block` to self. + // If `self` is the main queue, and current thread is main thread, the block + // will be invoked immediately instead of being dispatched. + func safeAsync(_ block: @escaping ()->()) { + if self === DispatchQueue.main && Thread.isMainThread { + block() + } else { + async { block() } + } + } +} diff --git a/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift b/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift new file mode 100755 index 0000000..97d3aab --- /dev/null +++ b/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift @@ -0,0 +1,415 @@ +// +// UIButton+Kingfisher.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/13. +// +// Copyright (c) 2017 Wei Wang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +// MARK: - Set Images +/** + * Set image to use in button from web for a specified state. + */ +extension Kingfisher where Base: UIButton { + /** + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and + completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + public func setImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + guard let resource = resource else { + base.setImage(placeholder, for: state) + setWebURL(nil, for: state) + completionHandler?(nil, nil, .none, nil) + return .empty + } + + let options = options ?? KingfisherEmptyOptionsInfo + if !options.keepCurrentImageWhileLoading { + base.setImage(placeholder, for: state) + } + + setWebURL(resource.downloadURL, for: state) + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, + progressBlock: { receivedSize, totalSize in + guard resource.downloadURL == self.webURL(for: state) else { + return + } + if let progressBlock = progressBlock { + progressBlock(receivedSize, totalSize) + } + }, + completionHandler: {[weak base] image, error, cacheType, imageURL in + DispatchQueue.main.safeAsync { + guard let strongBase = base, imageURL == self.webURL(for: state) else { + return + } + self.setImageTask(nil) + + if image != nil { + strongBase.setImage(image, for: state) + } + + completionHandler?(image, error, cacheType, imageURL) + } + }) + + setImageTask(task) + return task + } + + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func cancelImageDownloadTask() { + imageTask?.cancel() + } + + /** + Set the background image to use for a specified state with a resource, + a placeholder image, options progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + public func setBackgroundImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + guard let resource = resource else { + base.setBackgroundImage(placeholder, for: state) + setBackgroundWebURL(nil, for: state) + completionHandler?(nil, nil, .none, nil) + return .empty + } + + let options = options ?? KingfisherEmptyOptionsInfo + if !options.keepCurrentImageWhileLoading { + base.setBackgroundImage(placeholder, for: state) + } + + setBackgroundWebURL(resource.downloadURL, for: state) + let task = KingfisherManager.shared.retrieveImage( + with: resource, + options: options, + progressBlock: { receivedSize, totalSize in + guard resource.downloadURL == self.backgroundWebURL(for: state) else { + return + } + if let progressBlock = progressBlock { + progressBlock(receivedSize, totalSize) + } + }, + completionHandler: { [weak base] image, error, cacheType, imageURL in + DispatchQueue.main.safeAsync { + guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else { + return + } + self.setBackgroundImageTask(nil) + if image != nil { + strongBase.setBackgroundImage(image, for: state) + } + completionHandler?(image, error, cacheType, imageURL) + } + }) + + setBackgroundImageTask(task) + return task + } + + /** + Cancel the background image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + public func cancelBackgroundImageDownloadTask() { + backgroundImageTask?.cancel() + } + +} + +// MARK: - Associated Object +private var lastURLKey: Void? +private var imageTaskKey: Void? + +extension Kingfisher where Base: UIButton { + /** + Get the image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified image. + + - returns: Current URL for image. + */ + public func webURL(for state: UIControlState) -> URL? { + return webURLs[NSNumber(value:state.rawValue)] as? URL + } + + fileprivate func setWebURL(_ url: URL?, for state: UIControlState) { + webURLs[NSNumber(value:state.rawValue)] = url + } + + fileprivate var webURLs: NSMutableDictionary { + var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary + if dictionary == nil { + dictionary = NSMutableDictionary() + setWebURLs(dictionary!) + } + return dictionary! + } + + fileprivate func setWebURLs(_ URLs: NSMutableDictionary) { + objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var imageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask + } + + fileprivate func setImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + + +private var lastBackgroundURLKey: Void? +private var backgroundImageTaskKey: Void? + + +extension Kingfisher where Base: UIButton { + /** + Get the background image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified background image. + + - returns: Current URL for background image. + */ + public func backgroundWebURL(for state: UIControlState) -> URL? { + return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL + } + + fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) { + backgroundWebURLs[NSNumber(value:state.rawValue)] = url + } + + fileprivate var backgroundWebURLs: NSMutableDictionary { + var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary + if dictionary == nil { + dictionary = NSMutableDictionary() + setBackgroundWebURLs(dictionary!) + } + return dictionary! + } + + fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) { + objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + fileprivate var backgroundImageTask: RetrieveImageTask? { + return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask + } + + fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) { + objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + +// MARK: - Deprecated. Only for back compatibility. +/** +* Set image to use from web for a specified state. Deprecated. Use `kf` namespacing instead. +*/ +extension UIButton { + /** + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and + completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.setImage` instead.", + renamed: "kf.setImage") + public func kf_setImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setImage(with: resource, for: state, placeholder: placeholder, options: options, + progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Cancel the image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.cancelImageDownloadTask` instead.", + renamed: "kf.cancelImageDownloadTask") + public func kf_cancelImageDownloadTask() { kf.cancelImageDownloadTask() } + + /** + Set the background image to use for a specified state with a resource, + a placeholder image, options progress handler and completion handler. + + - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. + - parameter state: The state that uses the specified image. + - parameter placeholder: A placeholder image when retrieving the image at URL. + - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + - parameter progressBlock: Called when the image downloading progress gets updated. + - parameter completionHandler: Called when the image retrieved and set. + + - returns: A task represents the retrieving process. + + - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. + The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. + */ + @discardableResult + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.setBackgroundImage` instead.", + renamed: "kf.setBackgroundImage") + public func kf_setBackgroundImage(with resource: Resource?, + for state: UIControlState, + placeholder: UIImage? = nil, + options: KingfisherOptionsInfo? = nil, + progressBlock: DownloadProgressBlock? = nil, + completionHandler: CompletionHandler? = nil) -> RetrieveImageTask + { + return kf.setBackgroundImage(with: resource, for: state, placeholder: placeholder, options: options, + progressBlock: progressBlock, completionHandler: completionHandler) + } + + /** + Cancel the background image download task bounded to the image view if it is running. + Nothing will happen if the downloading has already finished. + */ + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.cancelBackgroundImageDownloadTask` instead.", + renamed: "kf.cancelBackgroundImageDownloadTask") + public func kf_cancelBackgroundImageDownloadTask() { kf.cancelBackgroundImageDownloadTask() } + + /** + Get the image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified image. + + - returns: Current URL for image. + */ + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.webURL` instead.", + renamed: "kf.webURL") + public func kf_webURL(for state: UIControlState) -> URL? { return kf.webURL(for: state) } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setWebURL") + fileprivate func kf_setWebURL(_ url: URL, for state: UIControlState) { kf.setWebURL(url, for: state) } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.webURLs") + fileprivate var kf_webURLs: NSMutableDictionary { return kf.webURLs } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setWebURLs") + fileprivate func kf_setWebURLs(_ URLs: NSMutableDictionary) { kf.setWebURLs(URLs) } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.imageTask") + fileprivate var kf_imageTask: RetrieveImageTask? { return kf.imageTask } + + @available(*, deprecated, message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setImageTask") + fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task) } + + /** + Get the background image URL binded to this button for a specified state. + + - parameter state: The state that uses the specified background image. + + - returns: Current URL for background image. + */ + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated. Use `button.kf.backgroundWebURL` instead.", + renamed: "kf.backgroundWebURL") + public func kf_backgroundWebURL(for state: UIControlState) -> URL? { return kf.backgroundWebURL(for: state) } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setBackgroundWebURL") + fileprivate func kf_setBackgroundWebURL(_ url: URL, for state: UIControlState) { + kf.setBackgroundWebURL(url, for: state) + } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.backgroundWebURLs") + fileprivate var kf_backgroundWebURLs: NSMutableDictionary { return kf.backgroundWebURLs } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setBackgroundWebURLs") + fileprivate func kf_setBackgroundWebURLs(_ URLs: NSMutableDictionary) { kf.setBackgroundWebURLs(URLs) } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.backgroundImageTask") + fileprivate var kf_backgroundImageTask: RetrieveImageTask? { return kf.backgroundImageTask } + + @available(*, deprecated, + message: "Extensions directly on UIButton are deprecated.",renamed: "kf.setBackgroundImageTask") + fileprivate func kf_setBackgroundImageTask(_ task: RetrieveImageTask?) { return kf.setBackgroundImageTask(task) } + +} diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..d67c7b1 --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,30 @@ +PODS: + - Kingfisher (3.5.0) + - ObjectMapper (2.2.3) + - SCLAlertView (0.7.0) + - SwiftOverlays (3.0.0) + - SwiftyAttributes (3.1.0) + - SwiftyJSON (3.1.4) + - Toaster (2.0.3) + +DEPENDENCIES: + - Kingfisher (~> 3.0) + - ObjectMapper (~> 2.2) + - SCLAlertView + - SwiftOverlays (~> 3.0.0) + - SwiftyAttributes + - SwiftyJSON + - Toaster (~> 2.0) + +SPEC CHECKSUMS: + Kingfisher: 7c62087039d93d26c54c8dd9b0b588d2ce55cef6 + ObjectMapper: d3b3de11267f5d971f390eb8d63dd509116a4329 + SCLAlertView: 8a14ffc40590c619e183eed2298b854385f30be1 + SwiftOverlays: 4cdd9e4f5c7817fb6348882059eb697e11685fad + SwiftyAttributes: f337a324cd21f1b18782ab85d3478ccae73f7593 + SwiftyJSON: c2842d878f95482ffceec5709abc3d05680c0220 + Toaster: 77c0b03d78e680bbbab0071c9c7ee392b0034010 + +PODFILE CHECKSUM: dc819e3ff4831b19945fa15b2eafcd86e0b0d9ac + +COCOAPODS: 1.2.0.beta.3 diff --git a/Pods/ObjectMapper/LICENSE b/Pods/ObjectMapper/LICENSE new file mode 100644 index 0000000..be48bc6 --- /dev/null +++ b/Pods/ObjectMapper/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2014 Hearst + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Pods/ObjectMapper/README-CN.md b/Pods/ObjectMapper/README-CN.md new file mode 100644 index 0000000..ee0d65a --- /dev/null +++ b/Pods/ObjectMapper/README-CN.md @@ -0,0 +1,502 @@ +# ObjectMapper-CN-Guide +> 文档由Swift老司机活动中心负责翻译,欢迎关注[@SwiftOldDriver](http://weibo.com/6062089411)。翻译有问题可以到 [ObjectMapper-CN-Guide](https://github.com/SwiftOldDriver/ObjectMapper-CN-Guide) 提 PR。 + +[ObjectMapper](https://github.com/Hearst-DD/ObjectMapper) 是一个使用 Swift 编写的用于 model 对象(类和结构体)和 JSON 之间转换的框架。 + +- [特性](#特性) +- [基础使用方法](#基础使用方法) +- [映射嵌套对象](#映射嵌套对象) +- [自定义转换规则](#自定义转换规则) +- [继承](#继承) +- [泛型对象](#泛型对象) +- [映射时的上下文对象](#映射时的上下文对象) +- [ObjectMapper + Alamofire](#objectmapper--alamofire) +- [ObjectMapper + Realm](#objectmapper--realm) +- [待完成](#待完成) +- [安装](#安装) + +# 特性: +- 把 JSON 映射成对象 +- 把对象映射 JSON +- 支持嵌套对象 (单独的成员变量、在数组或字典中都可以) +- 在转换过程支持自定义规则 +- 支持结构体( Struct ) +- [Immutable support](#immutablemappable-protocol-beta) (目前还在 beta ) + +# 基础使用方法 +为了支持映射,类或者结构体只需要实现```Mappable```协议。这个协议包含以下方法: +```swift +init?(map: Map) +mutating func mapping(map: Map) +``` +ObjectMapper使用自定义的```<-``` 运算符来声明成员变量和 JSON 的映射关系。 +```swift +class User: Mappable { + var username: String? + var age: Int? + var weight: Double! + var array: [AnyObject]? + var dictionary: [String : AnyObject] = [:] + var bestFriend: User? // 嵌套的 User 对象 + var friends: [User]? // Users 的数组 + var birthday: NSDate? + + required init?(map: Map) { + + } + + // Mappable + func mapping(map: Map) { + username <- map["username"] + age <- map["age"] + weight <- map["weight"] + array <- map["arr"] + dictionary <- map["dict"] + bestFriend <- map["best_friend"] + friends <- map["friends"] + birthday <- (map["birthday"], DateTransform()) + } +} + +struct Temperature: Mappable { + var celsius: Double? + var fahrenheit: Double? + + init?(map: Map) { + + } + + mutating func mapping(map: Map) { + celsius <- map["celsius"] + fahrenheit <- map["fahrenheit"] + } +} +``` + +一旦你的对象实现了 `Mappable`, ObjectMapper就可以让你轻松的实现和 JSON 之间的转换。 + +把 JSON 字符串转成 model 对象: + +```swift +let user = User(JSONString: JSONString) +``` + +把一个 model 转成 JSON 字符串: + +```swift +let JSONString = user.toJSONString(prettyPrint: true) +``` + +也可以使用`Mapper.swift`类来完成转换(这个类还额外提供了一些函数来处理一些特殊的情况: + +```swift +// 把 JSON 字符串转成 Model +let user = Mapper().map(JSONString: JSONString) +// 根据 Model 生成 JSON 字符串 +let JSONString = Mapper().toJSONString(user, prettyPrint: true) +``` + +ObjectMapper支持以下的类型映射到对象中: + +- `Int` +- `Bool` +- `Double` +- `Float` +- `String` +- `RawRepresentable` (枚举) +- `Array` +- `Dictionary` +- `Object` +- `Array` +- `Array>` +- `Set` +- `Dictionary` +- `Dictionary>` +- 以上所有的 Optional 类型 +- 以上所有的隐式强制解包类型(Implicitly Unwrapped Optional) + +## `Mappable` 协议 + +#### `mutating func mapping(map: Map)` +所有的映射最后都会调用到这个函数。当解析 JSON 时,这个函数会在对象创建成功后被执行。当生成 JSON 时就只有这个函数会被对象调用。 + +#### `init?(map: Map)` +这个可失败的初始化函数是 ObjectMapper 创建对象的时候使用的。开发者可以通过这个函数在映射前校验 JSON 。如果在这个方法里返回 nil 就不会执行 `mapping` 函数。可以通过传入的保存着 JSON 的 `Map` 对象进行校验: + +```swift +required init?(map: Map){ + // 检查 JSON 里是否有一定要有的 "name" 属性 + if map.JSONDictionary["name"] == nil { + return nil + } +} +``` + +## `StaticMappable` 协议 +`StaticMappable` 是 `Mappable` 之外的另一种选择。 这个协议可以让开发者通过一个静态函数初始化对象而不是通过 `init?(map: Map)`。 + +注意: `StaticMappable` 和 `Mappable` 都继承了 `BaseMappable` 协议。 `BaseMappable` 协议声明了 `mapping(map: Map)` 函数。 + +#### `static func objectForMapping(map: Map) -> BaseMappable?` +ObjectMapper 使用这个函数获取对象后进行映射。开发者需要在这个函数里返回一个实现 `BaseMappable` 对象的实例。这个函数也可以用于: + +- 在对象进行映射前校验 JSON +- 提供一个缓存过的对象用于映射 +- 返回另外一种类型的对象(当然是必须实现了 BaseMappable)用于映射。比如你可能通过检查 JSON 推断出用于映射的对象 ([看这个例子](https://github.com/Hearst-DD/ObjectMapper/blob/master/ObjectMapperTests/ClassClusterTests.swift#L62))。 + +如果你需要在 extension 里实现 ObjectMapper,你需要选择这个协议而不是 `Mappable` 。 + +## `ImmutableMappable` Protocol (Beta) + +> ⚠️ 这个特性还处于 Beta 阶段。正式发布时 API 可能会完全不同。 + +使用 `ImmutableMappable` 可以映射不可变的属性。下面的表格展示了 `ImmutableMappable` 和 `Mappable` 的不同: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ImmutableMappableMappable
Properties
+
+let id: Int
+let name: String?
+
+
+
+var id: Int!
+var name: String?
+
+
JSON -> Model
+
+init(map: Map) throws {
+  id   = try map.value("id")
+  name = try? map.value("name")
+}
+
+
+
+mutating func mapping(map: Map) {
+  id   <- map["id"]
+  name <- map["name"]
+}
+
+
Model -> JSON
+
+mutating func mapping(map: Map) {
+  id   >>> map["id"]
+  name >>> map["name"]
+}
+
+
+
+mutating func mapping(map: Map) {
+  id   <- map["id"]
+  name <- map["name"]
+}
+
+
Initializing
+
+try User(JSONString: JSONString)
+
+
+
+User(JSONString: JSONString)
+
+
+ +#### `init(map: Map) throws` + +这个可能抛出异常的初始化函数用于在提供的 `Map` 里映射不可变属性。每个不可变的初始化属性都要在这个初始化函数里初始化。 + +当发生下列情况时初始化函数会抛出一个错误: + +- `Map` 根据提供的键名获取不到对应值 +- `Map` 使用 `Transform` 后没有得到值 + +`ImmutableMappable` 使用 `Map.value(_:using:)` 方法从 `Map` 中获取值。因为可能抛出异常,这个方法在使用时需要使用 `try` 关键字。 `Optional` 的属性可以简单的用 `try?` 处理。 + +```swift +init(map: Map) throws { + name = try map.value("name") // throws an error when it fails + createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails + updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional + posts = (try? map.value("posts")) ?? [] // optional + default value +} +``` + +#### `mutating func mapping(map: Map)` + +这个方法是在 Model 转回 JSON 时调用的。因为不可变的属性不能被 `<-` 映射,所以映射回来时需要使用 `>>>` 。 + +```swift +mutating func mapping(map: Map) { + name >>> map["name"] + createdAt >>> (map["createdAt"], DateTransform()) + updatedAt >>> (map["updatedAt"], DateTransform()) + posts >>> map["posts"] +} +``` +# 轻松映射嵌套对象 + +ObjectMapper 支持使用点语法来轻松实现嵌套对象的映射。比如有如下的 JSON 字符串: + +```json +"distance" : { + "text" : "102 ft", + "value" : 31 +} +``` +你可以通过这种写法直接访问到嵌套对象: + +```swift +func mapping(map: Map) { + distance <- map["distance.value"] +} +``` +嵌套的键名也支持访问数组中的值。如果有一个返回的 JSON 是一个包含 distance 的数组,可以通过这种写法访问: + +``` +distance <- map["distances.0.value"] +``` +如果你的键名刚好含有 `.` 符号,你需要特别声明关闭上面提到的获取嵌套对象功能: + +```swift +func mapping(map: Map) { + identifier <- map["app.identifier", nested: false] +} +``` +如果刚好有嵌套的对象的键名还有 `.` ,可以在中间加入一个自定义的分割符([#629](https://github.com/Hearst-DD/ObjectMapper/pull/629)): +```swift +func mapping(map: Map) { + appName <- map["com.myapp.info->com.myapp.name", delimiter: "->"] +} +``` +这种情况的 JSON 是这样的: + +```json +"com.myapp.info" : { + "com.myapp.name" : "SwiftOldDriver" +} +``` + +# 自定义转换规则 +ObjectMapper 也支持在映射时自定义转换规则。如果要使用自定义转换,创建一个 tuple(元祖)包含 ```map["field_name"]``` 和你要使用的变换放在 ```<-``` 的右边: + +```swift +birthday <- (map["birthday"], DateTransform()) +``` +当解析 JSON 时上面的转换会把 JSON 里面的 Int 值转成一个 NSDate ,如果是对象转为 JSON 时,则会把 NSDate 对象转成 Int 值。 + +只要实现```TransformType``` 协议就可以轻松的创建自定义的转换规则: + +```swift +public protocol TransformType { + associatedtype Object + associatedtype JSON + + func transformFromJSON(_ value: Any?) -> Object? + func transformToJSON(_ value: Object?) -> JSON? +} +``` + +### TransformOf +大多数情况下你都可以使用框架提供的转换类 ```TransformOf``` 来快速的实现一个期望的转换。 ```TransformOf``` 的初始化需要两个类型和两个闭包。两个类型声明了转换的目标类型和源类型,闭包则实现具体转换逻辑。 + +举个例子,如果你想要把一个 JSON 字符串转成 Int ,你可以像这样使用 ```TransformOf``` : + +```swift +let transform = TransformOf(fromJSON: { (value: String?) -> Int? in + // 把值从 String? 转成 Int? + return Int(value!) +}, toJSON: { (value: Int?) -> String? in + // 把值从 Int? 转成 String? + if let value = value { + return String(value) + } + return nil +}) + +id <- (map["id"], transform) +``` +这是一种更省略的写法: + +```swift +id <- (map["id"], TransformOf(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } })) +``` +# 继承 + +实现了 ```Mappable``` 协议的类可以容易的被继承。当继承一个 mappable 的类时,使用这样的结构: + +```swift +class Base: Mappable { + var base: String? + + required init?(map: Map) { + + } + + func mapping(map: Map) { + base <- map["base"] + } +} + +class Subclass: Base { + var sub: String? + + required init?(map: Map) { + super.init(map) + } + + override func mapping(map: Map) { + super.mapping(map) + + sub <- map["sub"] + } +} +``` + +注意确认子类中的实现调用了父类中正确的初始化器和映射函数。 + +# 泛型对象 + +ObjectMapper 可以处理泛型只要这个泛型也实现了`Mappable`协议。看这个例子: + +```swift +class Result: Mappable { + var result: T? + + required init?(map: Map){ + + } + + func mapping(map: Map) { + result <- map["result"] + } +} + +let result = Mapper>().map(JSON) +``` +# 映射时的上下文对象 + +`Map` 是在映射时传入的对象,带有一个 optional `MapContext` 对象,开发者可以通过使用这个对象在映射时传入一些信息。 + +为了使用这个特性,需要先创建一个对象实现了 `MapContext` 协议(这个协议是空的),然后在初始化时传入 `Mapper` 中。 + +```swift +struct Context: MapContext { + var importantMappingInfo = "映射时需要知道的额外信息" +} + +class User: Mappable { + var name: String? + + required init?(map: Map){ + + } + + func mapping(map: Map){ + if let context = map.context as? Context { + // 获取到额外的信息 + } + } +} + +let context = Context() +let user = Mapper(context: context).map(JSONString) +``` + +#ObjectMapper + Alamofire + +如果网络层你使用的是 [Alamofire](https://github.com/Alamofire/Alamofire) ,并且你希望把返回的结果转换成 Swift 对象,你可以使用 [AlamofireObjectMapper](https://github.com/tristanhimmelman/AlamofireObjectMapper) 。这是一个使用 ObjectMapper 实现的把返回的 JSON 自动转成 Swift 对象的 Alamofire 的扩展。 + + +#ObjectMapper + Realm + +ObjectMapper 可以和 Realm 一起配合使用。使用下面的声明结构就可以使用 ObjectMapper 生成 Realm 对象: + +```swift +class Model: Object, Mappable { + dynamic var name = "" + + required convenience init?(map: Map) { + self.init() + } + + func mapping(map: Map) { + name <- map["name"] + } +} +``` + +如果你想要序列化相关联的 RealmObject,你可以使用 [ObjectMapper+Realm](https://github.com/jakenberg/ObjectMapper-Realm)。这是一个简单的 Realm 扩展,用于把任意的 JSON 序列化成 Realm 的类(ealm's List class。) + +注意:使用 ObjectMappers 的 `toJSON` 函数来生成 JSON 字符串只在 Realm 的写事务中有效(write transaction)。这是因为 ObjectMapper 在解析和生成时在映射函数( `<-` )中使用 `inout` 作为标记( flag )。Realm 会检测到标记并且强制要求 `toJSON` 函数只能在一个写的事务中调用,即使这个对象并没有被修改。 + +# 待完成 +- 改善错误的处理。可能使用 `throws` 来处理。 +- 相关类的文档完善 + +# 安装 +### Cocoapods +如果你的项目使用 [CocoaPods 0.36 及以上](http://blog.cocoapods.org/Pod-Authors-Guide-to-CocoaPods-Frameworks/) 的版本,你可以把下面内容添加到在 `Podfile` 中,将 ObjectMapper 添加到你的项目中: + +```ruby +pod 'ObjectMapper', '~> 2.2' +``` + +### Carthage +如果你的项目使用 [Carthage](https://github.com/Carthage/Carthage) ,你可以把下面的内容添加到 `Cartfile` 中,将 ObjectMapper 的依赖到你的项目中: + +``` +github "Hearst-DD/ObjectMapper" ~> 2.2 +``` + +### Swift Package Manager +如果你的项目使用 [Swift Package Manager](https://swift.org/package-manager/) ,那么你可以把下面内容添加到 `Package.swift` 中的 `dependencies` 数组中,将 ObjectMapper 的依赖到你的项目中: + +```swift +.Package(url: "https://github.com/Hearst-DD/ObjectMapper.git", majorVersion: 2, minor: 2), +``` + + +### Submodule +此外,ObjectMapper 也可以作为一个 submodule 添加到项目中: + +1. 打开终端,使用 `cd` 命令进入项目文件的根目录下,然后在终端中输入 `git submodule add https://github.com/Hearst-DD/ObjectMapper.git` ,把 ObjectMapper 作为项目的一个 [submodule](http://git-scm.com/docs/git-submodule) 添加进来。 +2. 打开 `ObjectMapper` 文件,并将 `ObjectMapper.xcodeproj` 拖进你 app 项目的文件导航中。 +3. 在 Xcode 中,文件导航中点击蓝色项目图标进入到 target 配置界面,在侧边栏的 "TARGETS" 下选择主工程对应的target。 +4. 确保 `ObjectMapper.framework` 的部署版本( deployment target )和主工程的部署版本保持一致。 +5. 在配置界面的顶部选项栏中,打开 "Build Phases" 面板。 +6. 展开 "Target Dependencies" 组,并添加 `ObjectMapper.framework` 。 +7. 点击面板左上角的 `+` 按钮,选择 "New Copy Files Phase"。将这个阶段重命名为 "Copy Frameworks",设置 "Destination" 为 "Frameworks",最后添加 `ObjectMapper.framework` 。 + + diff --git a/Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift b/Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift new file mode 100644 index 0000000..3cd5c59 --- /dev/null +++ b/Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift @@ -0,0 +1,40 @@ +// +// CustomDateFormatTransform.swift +// ObjectMapper +// +// Created by Dan McCracken on 3/8/15. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class CustomDateFormatTransform: DateFormatterTransform { + + public init(formatString: String) { + let formatter = DateFormatter() + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = formatString + + super.init(dateFormatter: formatter) + } +} diff --git a/Pods/ObjectMapper/Sources/DataTransform.swift b/Pods/ObjectMapper/Sources/DataTransform.swift new file mode 100644 index 0000000..d96f3d6 --- /dev/null +++ b/Pods/ObjectMapper/Sources/DataTransform.swift @@ -0,0 +1,50 @@ +// +// DataTransform.swift +// ObjectMapper +// +// Created by Yagrushkin, Evgeny on 8/30/16. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class DataTransform: TransformType { + public typealias Object = Data + public typealias JSON = String + + public init() {} + + open func transformFromJSON(_ value: Any?) -> Data? { + guard let string = value as? String else{ + return nil + } + return Data(base64Encoded: string) + } + + open func transformToJSON(_ value: Data?) -> String? { + guard let data = value else{ + return nil + } + return data.base64EncodedString() + } +} diff --git a/Pods/ObjectMapper/Sources/DateFormatterTransform.swift b/Pods/ObjectMapper/Sources/DateFormatterTransform.swift new file mode 100644 index 0000000..fa75f3b --- /dev/null +++ b/Pods/ObjectMapper/Sources/DateFormatterTransform.swift @@ -0,0 +1,54 @@ +// +// DateFormatterTransform.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2015-03-09. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class DateFormatterTransform: TransformType { + public typealias Object = Date + public typealias JSON = String + + public let dateFormatter: DateFormatter + + public init(dateFormatter: DateFormatter) { + self.dateFormatter = dateFormatter + } + + open func transformFromJSON(_ value: Any?) -> Date? { + if let dateString = value as? String { + return dateFormatter.date(from: dateString) + } + return nil + } + + open func transformToJSON(_ value: Date?) -> String? { + if let date = value { + return dateFormatter.string(from: date) + } + return nil + } +} diff --git a/Pods/ObjectMapper/Sources/DateTransform.swift b/Pods/ObjectMapper/Sources/DateTransform.swift new file mode 100644 index 0000000..2c175cb --- /dev/null +++ b/Pods/ObjectMapper/Sources/DateTransform.swift @@ -0,0 +1,55 @@ +// +// DateTransform.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2014-10-13. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class DateTransform: TransformType { + public typealias Object = Date + public typealias JSON = Double + + public init() {} + + open func transformFromJSON(_ value: Any?) -> Date? { + if let timeInt = value as? Double { + return Date(timeIntervalSince1970: TimeInterval(timeInt)) + } + + if let timeStr = value as? String { + return Date(timeIntervalSince1970: TimeInterval(atof(timeStr))) + } + + return nil + } + + open func transformToJSON(_ value: Date?) -> Double? { + if let date = value { + return Double(date.timeIntervalSince1970) + } + return nil + } +} diff --git a/Pods/ObjectMapper/Sources/DictionaryTransform.swift b/Pods/ObjectMapper/Sources/DictionaryTransform.swift new file mode 100644 index 0000000..e7b6ef3 --- /dev/null +++ b/Pods/ObjectMapper/Sources/DictionaryTransform.swift @@ -0,0 +1,58 @@ +// +// DictionaryTransform.swift +// ObjectMapper +// +// Created by Milen Halachev on 7/20/16. +// Copyright © 2016 hearst. All rights reserved. +// + +import Foundation + +///Transforms [String: AnyObject] <-> [Key: Value] where Key is RawRepresentable as String, Value is Mappable +public struct DictionaryTransform: TransformType where Key: Hashable, Key: RawRepresentable, Key.RawValue == String, Value: Mappable { + + public init() { + + } + + public func transformFromJSON(_ value: Any?) -> [Key: Value]? { + + guard let json = value as? [String: Any] else { + + return nil + } + + let result = json.reduce([:]) { (result, element) -> [Key: Value] in + + guard + let key = Key(rawValue: element.0), + let valueJSON = element.1 as? [String: Any], + let value = Value(JSON: valueJSON) + else { + + return result + } + + var result = result + result[key] = value + return result + } + + return result + } + + public func transformToJSON(_ value: [Key: Value]?) -> Any? { + + let result = value?.reduce([:]) { (result, element) -> [String: Any] in + + let key = element.0.rawValue + let value = element.1.toJSON() + + var result = result + result[key] = value + return result + } + + return result + } +} diff --git a/Pods/ObjectMapper/Sources/EnumOperators.swift b/Pods/ObjectMapper/Sources/EnumOperators.swift new file mode 100644 index 0000000..5a1a667 --- /dev/null +++ b/Pods/ObjectMapper/Sources/EnumOperators.swift @@ -0,0 +1,91 @@ +// +// EnumOperators.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2016-09-26. +// Copyright © 2016 hearst. All rights reserved. +// + +import Foundation + + +// MARK:- Raw Representable types + +/// Object of Raw Representable type +public func <- (left: inout T, right: Map) { + left <- (right, EnumTransform()) +} + +public func >>> (left: T, right: Map) { + left >>> (right, EnumTransform()) +} + + +/// Optional Object of Raw Representable type +public func <- (left: inout T?, right: Map) { + left <- (right, EnumTransform()) +} + +public func >>> (left: T?, right: Map) { + left >>> (right, EnumTransform()) +} + + +/// Implicitly Unwrapped Optional Object of Raw Representable type +public func <- (left: inout T!, right: Map) { + left <- (right, EnumTransform()) +} + +// MARK:- Arrays of Raw Representable type + +/// Array of Raw Representable object +public func <- (left: inout [T], right: Map) { + left <- (right, EnumTransform()) +} + +public func >>> (left: [T], right: Map) { + left >>> (right, EnumTransform()) +} + + +/// Array of Raw Representable object +public func <- (left: inout [T]?, right: Map) { + left <- (right, EnumTransform()) +} + +public func >>> (left: [T]?, right: Map) { + left >>> (right, EnumTransform()) +} + + +/// Array of Raw Representable object +public func <- (left: inout [T]!, right: Map) { + left <- (right, EnumTransform()) +} + +// MARK:- Dictionaries of Raw Representable type + +/// Dictionary of Raw Representable object +public func <- (left: inout [String: T], right: Map) { + left <- (right, EnumTransform()) +} + +public func >>> (left: [String: T], right: Map) { + left >>> (right, EnumTransform()) +} + + +/// Dictionary of Raw Representable object +public func <- (left: inout [String: T]?, right: Map) { + left <- (right, EnumTransform()) +} + +public func >>> (left: [String: T]?, right: Map) { + left >>> (right, EnumTransform()) +} + + +/// Dictionary of Raw Representable object +public func <- (left: inout [String: T]!, right: Map) { + left <- (right, EnumTransform()) +} diff --git a/Pods/ObjectMapper/Sources/EnumTransform.swift b/Pods/ObjectMapper/Sources/EnumTransform.swift new file mode 100644 index 0000000..43e4ce7 --- /dev/null +++ b/Pods/ObjectMapper/Sources/EnumTransform.swift @@ -0,0 +1,50 @@ +// +// EnumTransform.swift +// ObjectMapper +// +// Created by Kaan Dedeoglu on 3/20/15. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class EnumTransform: TransformType { + public typealias Object = T + public typealias JSON = T.RawValue + + public init() {} + + open func transformFromJSON(_ value: Any?) -> T? { + if let raw = value as? T.RawValue { + return T(rawValue: raw) + } + return nil + } + + open func transformToJSON(_ value: T?) -> T.RawValue? { + if let obj = value { + return obj.rawValue + } + return nil + } +} diff --git a/Pods/ObjectMapper/Sources/FromJSON.swift b/Pods/ObjectMapper/Sources/FromJSON.swift new file mode 100755 index 0000000..7cb4655 --- /dev/null +++ b/Pods/ObjectMapper/Sources/FromJSON.swift @@ -0,0 +1,181 @@ +// +// FromJSON.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2014-10-09. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +internal final class FromJSON { + + /// Basic type + class func basicType(_ field: inout FieldType, object: FieldType?) { + if let value = object { + field = value + } + } + + /// optional basic type + class func optionalBasicType(_ field: inout FieldType?, object: FieldType?) { + field = object + } + + /// Implicitly unwrapped optional basic type + class func optionalBasicType(_ field: inout FieldType!, object: FieldType?) { + field = object + } + + /// Mappable object + class func object(_ field: inout N, map: Map) { + if map.toObject { + _ = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field) + } else if let value: N = Mapper(context: map.context).map(JSONObject: map.currentValue) { + field = value + } + } + + /// Optional Mappable Object + + class func optionalObject(_ field: inout N?, map: Map) { + if let field = field , map.toObject && map.currentValue != nil { + _ = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field) + } else { + field = Mapper(context: map.context).map(JSONObject: map.currentValue) + } + } + + /// Implicitly unwrapped Optional Mappable Object + class func optionalObject(_ field: inout N!, map: Map) { + if let field = field , map.toObject && map.currentValue != nil { + _ = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field) + } else { + field = Mapper(context: map.context).map(JSONObject: map.currentValue) + } + } + + /// mappable object array + class func objectArray(_ field: inout Array, map: Map) { + if let objects = Mapper(context: map.context).mapArray(JSONObject: map.currentValue) { + field = objects + } + } + + /// optional mappable object array + + class func optionalObjectArray(_ field: inout Array?, map: Map) { + if let objects: Array = Mapper(context: map.context).mapArray(JSONObject: map.currentValue) { + field = objects + } else { + field = nil + } + } + + /// Implicitly unwrapped optional mappable object array + class func optionalObjectArray(_ field: inout Array!, map: Map) { + if let objects: Array = Mapper(context: map.context).mapArray(JSONObject: map.currentValue) { + field = objects + } else { + field = nil + } + } + + /// mappable object array + class func twoDimensionalObjectArray(_ field: inout Array>, map: Map) { + if let objects = Mapper(context: map.context).mapArrayOfArrays(JSONObject: map.currentValue) { + field = objects + } + } + + /// optional mappable 2 dimentional object array + class func optionalTwoDimensionalObjectArray(_ field: inout Array>?, map: Map) { + field = Mapper(context: map.context).mapArrayOfArrays(JSONObject: map.currentValue) + } + + /// Implicitly unwrapped optional 2 dimentional mappable object array + class func optionalTwoDimensionalObjectArray(_ field: inout Array>!, map: Map) { + field = Mapper(context: map.context).mapArrayOfArrays(JSONObject: map.currentValue) + } + + /// Dctionary containing Mappable objects + class func objectDictionary(_ field: inout Dictionary, map: Map) { + if map.toObject { + _ = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field) + } else { + if let objects = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue) { + field = objects + } + } + } + + /// Optional dictionary containing Mappable objects + class func optionalObjectDictionary(_ field: inout Dictionary?, map: Map) { + if let field = field , map.toObject && map.currentValue != nil { + _ = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field) + } else { + field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue) + } + } + + /// Implicitly unwrapped Dictionary containing Mappable objects + class func optionalObjectDictionary(_ field: inout Dictionary!, map: Map) { + if let field = field , map.toObject && map.currentValue != nil { + _ = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field) + } else { + field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue) + } + } + + /// Dictionary containing Array of Mappable objects + class func objectDictionaryOfArrays(_ field: inout Dictionary, map: Map) { + if let objects = Mapper(context: map.context).mapDictionaryOfArrays(JSONObject: map.currentValue) { + field = objects + } + } + + /// Optional Dictionary containing Array of Mappable objects + class func optionalObjectDictionaryOfArrays(_ field: inout Dictionary?, map: Map) { + field = Mapper(context: map.context).mapDictionaryOfArrays(JSONObject: map.currentValue) + } + + /// Implicitly unwrapped Dictionary containing Array of Mappable objects + class func optionalObjectDictionaryOfArrays(_ field: inout Dictionary!, map: Map) { + field = Mapper(context: map.context).mapDictionaryOfArrays(JSONObject: map.currentValue) + } + + /// mappable object Set + class func objectSet(_ field: inout Set, map: Map) { + if let objects = Mapper(context: map.context).mapSet(JSONObject: map.currentValue) { + field = objects + } + } + + /// optional mappable object array + class func optionalObjectSet(_ field: inout Set?, map: Map) { + field = Mapper(context: map.context).mapSet(JSONObject: map.currentValue) + } + + /// Implicitly unwrapped optional mappable object array + class func optionalObjectSet(_ field: inout Set!, map: Map) { + field = Mapper(context: map.context).mapSet(JSONObject: map.currentValue) + } +} diff --git a/Pods/ObjectMapper/Sources/HexColorTransform.swift b/Pods/ObjectMapper/Sources/HexColorTransform.swift new file mode 100644 index 0000000..2dce348 --- /dev/null +++ b/Pods/ObjectMapper/Sources/HexColorTransform.swift @@ -0,0 +1,116 @@ +// +// HexColorTransform.swift +// ObjectMapper +// +// Created by Vitaliy Kuzmenko on 10/10/16. +// Copyright © 2016 hearst. All rights reserved. +// + +#if os(iOS) || os(tvOS) || os(watchOS) +import UIKit +#elseif os(macOS) +import Cocoa +#endif + +#if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) +open class HexColorTransform: TransformType { + + #if os(iOS) || os(tvOS) || os(watchOS) + public typealias Object = UIColor + #else + public typealias Object = NSColor + #endif + + public typealias JSON = String + + var prefix: Bool = false + + var alpha: Bool = false + + public init(prefixToJSON: Bool = false, alphaToJSON: Bool = false) { + alpha = alphaToJSON + prefix = prefixToJSON + } + + open func transformFromJSON(_ value: Any?) -> Object? { + if let rgba = value as? String { + if rgba.hasPrefix("#") { + let index = rgba.characters.index(rgba.startIndex, offsetBy: 1) + let hex = rgba.substring(from: index) + return getColor(hex: hex) + } else { + return getColor(hex: rgba) + } + } + return nil + } + + open func transformToJSON(_ value: Object?) -> JSON? { + if let value = value { + return hexString(color: value) + } + return nil + } + + fileprivate func hexString(color: Object) -> String { + let comps = color.cgColor.components! + let r = Int(comps[0] * 255) + let g = Int(comps[1] * 255) + let b = Int(comps[2] * 255) + let a = Int(comps[3] * 255) + var hexString: String = "" + if prefix { + hexString = "#" + } + hexString += String(format: "%02X%02X%02X", r, g, b) + + if alpha { + hexString += String(format: "%02X", a) + } + return hexString + } + + fileprivate func getColor(hex: String) -> Object? { + var red: CGFloat = 0.0 + var green: CGFloat = 0.0 + var blue: CGFloat = 0.0 + var alpha: CGFloat = 1.0 + + let scanner = Scanner(string: hex) + var hexValue: CUnsignedLongLong = 0 + if scanner.scanHexInt64(&hexValue) { + switch (hex.characters.count) { + case 3: + red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 + green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 + blue = CGFloat(hexValue & 0x00F) / 15.0 + case 4: + red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 + green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 + blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 + alpha = CGFloat(hexValue & 0x000F) / 15.0 + case 6: + red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 + green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 + blue = CGFloat(hexValue & 0x0000FF) / 255.0 + case 8: + red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 + green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 + blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 + alpha = CGFloat(hexValue & 0x000000FF) / 255.0 + default: + // Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8 + return nil + } + } else { + // "Scan hex error + return nil + } + #if os(iOS) || os(tvOS) || os(watchOS) + return UIColor(red: red, green: green, blue: blue, alpha: alpha) + #else + return NSColor(calibratedRed: red, green: green, blue: blue, alpha: alpha) + #endif + } +} +#endif diff --git a/Pods/ObjectMapper/Sources/ISO8601DateTransform.swift b/Pods/ObjectMapper/Sources/ISO8601DateTransform.swift new file mode 100644 index 0000000..5a17b73 --- /dev/null +++ b/Pods/ObjectMapper/Sources/ISO8601DateTransform.swift @@ -0,0 +1,41 @@ +// +// ISO8601DateTransform.swift +// ObjectMapper +// +// Created by Jean-Pierre Mouilleseaux on 21 Nov 2014. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class ISO8601DateTransform: DateFormatterTransform { + + public init() { + let formatter = DateFormatter() + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" + + super.init(dateFormatter: formatter) + } + +} diff --git a/Pods/ObjectMapper/Sources/ImmutableMappable.swift b/Pods/ObjectMapper/Sources/ImmutableMappable.swift new file mode 100644 index 0000000..ae99179 --- /dev/null +++ b/Pods/ObjectMapper/Sources/ImmutableMappable.swift @@ -0,0 +1,275 @@ +// +// ImmutableMappble.swift +// ObjectMapper +// +// Created by Suyeol Jeon on 23/09/2016. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +public protocol ImmutableMappable: BaseMappable { + init(map: Map) throws +} + +public extension ImmutableMappable { + + /// Implement this method to support object -> JSON transform. + public func mapping(map: Map) {} + + /// Initializes object from a JSON String + public init(JSONString: String, context: MapContext? = nil) throws { + self = try Mapper(context: context).map(JSONString: JSONString) + } + + /// Initializes object from a JSON Dictionary + public init(JSON: [String: Any], context: MapContext? = nil) throws { + self = try Mapper(context: context).map(JSON: JSON) + } + + /// Initializes object from a JSONObject + public init(JSONObject: Any, context: MapContext? = nil) throws { + self = try Mapper(context: context).map(JSONObject: JSONObject) + } + +} + +public extension Map { + + fileprivate func currentValue(for key: String, nested: Bool? = nil, delimiter: String = ".") -> Any? { + let isNested = nested ?? key.contains(delimiter) + return self[key, nested: isNested, delimiter: delimiter].currentValue + } + + // MARK: Basic + + /// Returns a value or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let value = currentValue as? T else { + throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '\(T.self)'", file: file, function: function, line: line) + } + return value + } + + /// Returns a transformed value or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> Transform.Object { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let value = transform.transformFromJSON(currentValue) else { + throw MapError(key: key, currentValue: currentValue, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) + } + return value + } + + /// Returns a RawRepresentable type or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T { + return try self.value(key, nested: nested, delimiter: delimiter, using: EnumTransform(), file: file, function: function, line: line) + } + + // MARK: BaseMappable + + /// Returns a `BaseMappable` object or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let JSONObject = currentValue else { + throw MapError(key: key, currentValue: currentValue, reason: "Found unexpected nil value", file: file, function: function, line: line) + } + return try Mapper(context: context).mapOrFail(JSONObject: JSONObject) + } + + // MARK: [BaseMappable] + + /// Returns a `[BaseMappable]` or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [T] { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let jsonArray = currentValue as? [Any] else { + throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[Any]'", file: file, function: function, line: line) + } + return try jsonArray.enumerated().map { i, JSONObject -> T in + return try Mapper(context: context).mapOrFail(JSONObject: JSONObject) + } + } + + /// Returns a `[BaseMappable]` using transform or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [Transform.Object] { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let jsonArray = currentValue as? [Any] else { + throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[Any]'", file: file, function: function, line: line) + } + return try jsonArray.enumerated().map { i, json -> Transform.Object in + guard let object = transform.transformFromJSON(json) else { + throw MapError(key: "\(key)[\(i)]", currentValue: json, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) + } + return object + } + } + + // MARK: [String: BaseMappable] + + /// Returns a `[String: BaseMappable]` or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [String: T] { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let jsonDictionary = currentValue as? [String: Any] else { + throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[String: Any]'", file: file, function: function, line: line) + } + var value: [String: T] = [:] + for (key, json) in jsonDictionary { + value[key] = try Mapper(context: context).mapOrFail(JSONObject: json) + } + return value + } + + /// Returns a `[String: BaseMappable]` using transform or throws an error. + public func value(_ key: String, nested: Bool? = nil, delimiter: String = ".", using transform: Transform, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [String: Transform.Object] { + let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) + guard let jsonDictionary = currentValue as? [String: Any] else { + throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '[String: Any]'", file: file, function: function, line: line) + } + var value: [String: Transform.Object] = [:] + for (key, json) in jsonDictionary { + guard let object = transform.transformFromJSON(json) else { + throw MapError(key: key, currentValue: json, reason: "Cannot transform to '\(Transform.Object.self)' using \(transform)", file: file, function: function, line: line) + } + value[key] = object + } + return value + } + +} + +public extension Mapper where N: ImmutableMappable { + + public func map(JSON: [String: Any]) throws -> N { + return try self.mapOrFail(JSON: JSON) + } + + public func map(JSONString: String) throws -> N { + return try mapOrFail(JSONString: JSONString) + } + + public func map(JSONObject: Any) throws -> N { + return try mapOrFail(JSONObject: JSONObject) + } + + // MARK: Array mapping functions + + public func mapArray(JSONArray: [[String: Any]]) throws -> [N] { + return try JSONArray.flatMap(mapOrFail) + } + + public func mapArray(JSONString: String) throws -> [N] { + guard let JSONObject = Mapper.parseJSONString(JSONString: JSONString) else { + throw MapError(key: nil, currentValue: JSONString, reason: "Cannot convert string into Any'") + } + + return try mapArray(JSONObject: JSONObject) + } + + public func mapArray(JSONObject: Any) throws -> [N] { + guard let JSONArray = JSONObject as? [[String: Any]] else { + throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[[String: Any]]'") + } + + return try mapArray(JSONArray: JSONArray) + } + + // MARK: Dictionary mapping functions + + public func mapDictionary(JSONString: String) throws -> [String: N] { + guard let JSONObject = Mapper.parseJSONString(JSONString: JSONString) else { + throw MapError(key: nil, currentValue: JSONString, reason: "Cannot convert string into Any'") + } + + return try mapDictionary(JSONObject: JSONObject) + } + + public func mapDictionary(JSONObject: Any?) throws -> [String: N] { + guard let JSON = JSONObject as? [String: [String: Any]] else { + throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: [String: Any]]''") + } + + return try mapDictionary(JSON: JSON) + } + + public func mapDictionary(JSON: [String: [String: Any]]) throws -> [String: N] { + return try JSON.filterMap(mapOrFail) + } + + // MARK: Dictinoary of arrays mapping functions + + public func mapDictionaryOfArrays(JSONObject: Any?) throws -> [String: [N]] { + guard let JSON = JSONObject as? [String: [[String: Any]]] else { + throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: [String: Any]]''") + } + return try mapDictionaryOfArrays(JSON: JSON) + } + + public func mapDictionaryOfArrays(JSON: [String: [[String: Any]]]) throws -> [String: [N]] { + return try JSON.filterMap { array -> [N] in + try mapArray(JSONArray: array) + } + } + + // MARK: 2 dimentional array mapping functions + + public func mapArrayOfArrays(JSONObject: Any?) throws -> [[N]] { + guard let JSONArray = JSONObject as? [[[String: Any]]] else { + throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[[[String: Any]]]''") + } + return try JSONArray.map(mapArray) + } + +} + +internal extension Mapper where N: BaseMappable { + + internal func mapOrFail(JSON: [String: Any]) throws -> N { + let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues) + + // Check if object is ImmutableMappable, if so use ImmutableMappable protocol for mapping + if let klass = N.self as? ImmutableMappable.Type, + var object = try klass.init(map: map) as? N { + object.mapping(map: map) + return object + } + + // If not, map the object the standard way + guard let value = self.map(JSON: JSON) else { + throw MapError(key: nil, currentValue: JSON, reason: "Cannot map to '\(N.self)'") + } + return value + } + + internal func mapOrFail(JSONString: String) throws -> N { + guard let JSON = Mapper.parseJSONStringIntoDictionary(JSONString: JSONString) else { + throw MapError(key: nil, currentValue: JSONString, reason: "Cannot parse into '[String: Any]'") + } + return try mapOrFail(JSON: JSON) + } + + internal func mapOrFail(JSONObject: Any) throws -> N { + guard let JSON = JSONObject as? [String: Any] else { + throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: Any]'") + } + return try mapOrFail(JSON: JSON) + } + +} diff --git a/Pods/ObjectMapper/Sources/Map.swift b/Pods/ObjectMapper/Sources/Map.swift new file mode 100644 index 0000000..22f87b5 --- /dev/null +++ b/Pods/ObjectMapper/Sources/Map.swift @@ -0,0 +1,181 @@ +// +// Map.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2015-10-09. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +import Foundation + +/// MapContext is available for developers who wish to pass information around during the mapping process. +public protocol MapContext { + +} + +/// A class used for holding mapping data +public final class Map { + public let mappingType: MappingType + + public internal(set) var JSON: [String: Any] = [:] + public internal(set) var isKeyPresent = false + public internal(set) var currentValue: Any? + public internal(set) var currentKey: String? + var keyIsNested = false + public internal(set) var nestedKeyDelimiter: String = "." + public var context: MapContext? + public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set. + + let toObject: Bool // indicates whether the mapping is being applied to an existing object + + public init(mappingType: MappingType, JSON: [String: Any], toObject: Bool = false, context: MapContext? = nil, shouldIncludeNilValues: Bool = false) { + + self.mappingType = mappingType + self.JSON = JSON + self.toObject = toObject + self.context = context + self.shouldIncludeNilValues = shouldIncludeNilValues + } + + /// Sets the current mapper value and key. + /// The Key paramater can be a period separated string (ex. "distance.value") to access sub objects. + public subscript(key: String) -> Map { + // save key and value associated to it + return self[key, delimiter: ".", ignoreNil: false] + } + + public subscript(key: String, delimiter delimiter: String) -> Map { + let nested = key.contains(delimiter) + return self[key, nested: nested, delimiter: delimiter, ignoreNil: false] + } + + public subscript(key: String, nested nested: Bool) -> Map { + return self[key, nested: nested, delimiter: ".", ignoreNil: false] + } + + public subscript(key: String, nested nested: Bool, delimiter delimiter: String) -> Map { + return self[key, nested: nested, delimiter: delimiter, ignoreNil: false] + } + + public subscript(key: String, ignoreNil ignoreNil: Bool) -> Map { + return self[key, delimiter: ".", ignoreNil: ignoreNil] + } + + public subscript(key: String, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { + let nested = key.contains(delimiter) + return self[key, nested: nested, delimiter: delimiter, ignoreNil: ignoreNil] + } + + public subscript(key: String, nested nested: Bool, ignoreNil ignoreNil: Bool) -> Map { + return self[key, nested: nested, delimiter: ".", ignoreNil: ignoreNil] + } + + public subscript(key: String, nested nested: Bool, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { + // save key and value associated to it + currentKey = key + keyIsNested = nested + nestedKeyDelimiter = delimiter + + if mappingType == .fromJSON { + // check if a value exists for the current key + // do this pre-check for performance reasons + if nested == false { + let object = JSON[key] + let isNSNull = object is NSNull + isKeyPresent = isNSNull ? true : object != nil + currentValue = isNSNull ? nil : object + } else { + // break down the components of the key that are separated by . + (isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON) + } + + // update isKeyPresent if ignoreNil is true + if ignoreNil && currentValue == nil { + isKeyPresent = false + } + } + + return self + } + + public func value() -> T? { + return currentValue as? T + } + +} + +/// Fetch value from JSON dictionary, loop through keyPathComponents until we reach the desired object +private func valueFor(_ keyPathComponents: ArraySlice, dictionary: [String: Any]) -> (Bool, Any?) { + // Implement it as a tail recursive function. + if keyPathComponents.isEmpty { + return (false, nil) + } + + if let keyPath = keyPathComponents.first { + let object = dictionary[keyPath] + if object is NSNull { + return (true, nil) + } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { + let tail = keyPathComponents.dropFirst() + return valueFor(tail, dictionary: dict) + } else if keyPathComponents.count > 1, let array = object as? [Any] { + let tail = keyPathComponents.dropFirst() + return valueFor(tail, array: array) + } else { + return (object != nil, object) + } + } + + return (false, nil) +} + +/// Fetch value from JSON Array, loop through keyPathComponents them until we reach the desired object +private func valueFor(_ keyPathComponents: ArraySlice, array: [Any]) -> (Bool, Any?) { + // Implement it as a tail recursive function. + + if keyPathComponents.isEmpty { + return (false, nil) + } + + //Try to convert keypath to Int as index + if let keyPath = keyPathComponents.first, + let index = Int(keyPath) , index >= 0 && index < array.count { + + let object = array[index] + + if object is NSNull { + return (true, nil) + } else if keyPathComponents.count > 1, let array = object as? [Any] { + let tail = keyPathComponents.dropFirst() + return valueFor(tail, array: array) + } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { + let tail = keyPathComponents.dropFirst() + return valueFor(tail, dictionary: dict) + } else { + return (true, object) + } + } + + return (false, nil) +} diff --git a/Pods/ObjectMapper/Sources/MapError.swift b/Pods/ObjectMapper/Sources/MapError.swift new file mode 100644 index 0000000..21e20cb --- /dev/null +++ b/Pods/ObjectMapper/Sources/MapError.swift @@ -0,0 +1,68 @@ +// +// MapError.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2016-09-26. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +public struct MapError: Error { + public var key: String? + public var currentValue: Any? + public var reason: String? + public var file: StaticString? + public var function: StaticString? + public var line: UInt? + + public init(key: String?, currentValue: Any?, reason: String?, file: StaticString? = nil, function: StaticString? = nil, line: UInt? = nil) { + self.key = key + self.currentValue = currentValue + self.reason = reason + self.file = file + self.function = function + self.line = line + } +} + +extension MapError: CustomStringConvertible { + + private var location: String? { + guard let file = file, let function = function, let line = line else { return nil } + let fileName = ((String(describing: file).components(separatedBy: "/").last ?? "").components(separatedBy: ".").first ?? "") + return "\(fileName).\(function):\(line)" + } + + public var description: String { + let info: [(String, Any?)] = [ + ("- reason", reason), + ("- location", location), + ("- key", key), + ("- currentValue", currentValue), + ] + let infoString = info.map { "\($0): \($1 ?? "nil")" }.joined(separator: "\n") + return "Got an error while mapping.\n\(infoString)" + } + +} diff --git a/Pods/ObjectMapper/Sources/Mappable.swift b/Pods/ObjectMapper/Sources/Mappable.swift new file mode 100644 index 0000000..f8abb92 --- /dev/null +++ b/Pods/ObjectMapper/Sources/Mappable.swift @@ -0,0 +1,139 @@ +// +// Mappable.swift +// ObjectMapper +// +// Created by Scott Hoyt on 10/25/15. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +/// BaseMappable should not be implemented directly. Mappable or StaticMappable should be used instead +public protocol BaseMappable { + /// This function is where all variable mappings should occur. It is executed by Mapper during the mapping (serialization and deserialization) process. + mutating func mapping(map: Map) +} + +public protocol Mappable: BaseMappable { + /// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping at this point + init?(map: Map) +} + +public protocol StaticMappable: BaseMappable { + /// This is function that can be used to: + /// 1) provide an existing cached object to be used for mapping + /// 2) return an object of another class (which conforms to BaseMappable) to be used for mapping. For instance, you may inspect the JSON to infer the type of object that should be used for any given mapping + static func objectForMapping(map: Map) -> BaseMappable? +} + +public extension BaseMappable { + + /// Initializes object from a JSON String + public init?(JSONString: String, context: MapContext? = nil) { + if let obj: Self = Mapper(context: context).map(JSONString: JSONString) { + self = obj + } else { + return nil + } + } + + /// Initializes object from a JSON Dictionary + public init?(JSON: [String: Any], context: MapContext? = nil) { + if let obj: Self = Mapper(context: context).map(JSON: JSON) { + self = obj + } else { + return nil + } + } + + /// Returns the JSON Dictionary for the object + public func toJSON() -> [String: Any] { + return Mapper().toJSON(self) + } + + /// Returns the JSON String for the object + public func toJSONString(prettyPrint: Bool = false) -> String? { + return Mapper().toJSONString(self, prettyPrint: prettyPrint) + } +} + +public extension Array where Element: BaseMappable { + + /// Initialize Array from a JSON String + public init?(JSONString: String, context: MapContext? = nil) { + if let obj: [Element] = Mapper(context: context).mapArray(JSONString: JSONString) { + self = obj + } else { + return nil + } + } + + /// Initialize Array from a JSON Array + public init?(JSONArray: [[String: Any]], context: MapContext? = nil) { + if let obj: [Element] = Mapper(context: context).mapArray(JSONArray: JSONArray) { + self = obj + } else { + return nil + } + } + + /// Returns the JSON Array + public func toJSON() -> [[String: Any]] { + return Mapper().toJSONArray(self) + } + + /// Returns the JSON String for the object + public func toJSONString(prettyPrint: Bool = false) -> String? { + return Mapper().toJSONString(self, prettyPrint: prettyPrint) + } +} + +public extension Set where Element: BaseMappable { + + /// Initializes a set from a JSON String + public init?(JSONString: String, context: MapContext? = nil) { + if let obj: Set = Mapper(context: context).mapSet(JSONString: JSONString) { + self = obj + } else { + return nil + } + } + + /// Initializes a set from JSON + public init?(JSONArray: [[String: Any]], context: MapContext? = nil) { + guard let obj = Mapper(context: context).mapSet(JSONArray: JSONArray) as Set? else { + return nil + } + self = obj + } + + /// Returns the JSON Set + public func toJSON() -> [[String: Any]] { + return Mapper().toJSONSet(self) + } + + /// Returns the JSON String for the object + public func toJSONString(prettyPrint: Bool = false) -> String? { + return Mapper().toJSONString(self, prettyPrint: prettyPrint) + } +} diff --git a/Pods/ObjectMapper/Sources/Mapper.swift b/Pods/ObjectMapper/Sources/Mapper.swift new file mode 100755 index 0000000..9071acc --- /dev/null +++ b/Pods/ObjectMapper/Sources/Mapper.swift @@ -0,0 +1,433 @@ +// +// Mapper.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2014-10-09. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +public enum MappingType { + case fromJSON + case toJSON +} + +/// The Mapper class provides methods for converting Model objects to JSON and methods for converting JSON to Model objects +public final class Mapper { + + public var context: MapContext? + public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set. + + public init(context: MapContext? = nil, shouldIncludeNilValues: Bool = false){ + self.context = context + self.shouldIncludeNilValues = shouldIncludeNilValues + } + + // MARK: Mapping functions that map to an existing object toObject + + /// Maps a JSON object to an existing Mappable object if it is a JSON dictionary, or returns the passed object as is + public func map(JSONObject: Any?, toObject object: N) -> N { + if let JSON = JSONObject as? [String: Any] { + return map(JSON: JSON, toObject: object) + } + + return object + } + + /// Map a JSON string onto an existing object + public func map(JSONString: String, toObject object: N) -> N { + if let JSON = Mapper.parseJSONStringIntoDictionary(JSONString: JSONString) { + return map(JSON: JSON, toObject: object) + } + return object + } + + /// Maps a JSON dictionary to an existing object that conforms to Mappable. + /// Usefull for those pesky objects that have crappy designated initializers like NSManagedObject + public func map(JSON: [String: Any], toObject object: N) -> N { + var mutableObject = object + let map = Map(mappingType: .fromJSON, JSON: JSON, toObject: true, context: context, shouldIncludeNilValues: shouldIncludeNilValues) + mutableObject.mapping(map: map) + return mutableObject + } + + //MARK: Mapping functions that create an object + + /// Map a JSON string to an object that conforms to Mappable + public func map(JSONString: String) -> N? { + if let JSON = Mapper.parseJSONStringIntoDictionary(JSONString: JSONString) { + return map(JSON: JSON) + } + + return nil + } + + /// Maps a JSON object to a Mappable object if it is a JSON dictionary or NSString, or returns nil. + public func map(JSONObject: Any?) -> N? { + if let JSON = JSONObject as? [String: Any] { + return map(JSON: JSON) + } + + return nil + } + + /// Maps a JSON dictionary to an object that conforms to Mappable + public func map(JSON: [String: Any]) -> N? { + let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues) + + if let klass = N.self as? StaticMappable.Type { // Check if object is StaticMappable + if var object = klass.objectForMapping(map: map) as? N { + object.mapping(map: map) + return object + } + } else if let klass = N.self as? Mappable.Type { // Check if object is Mappable + if var object = klass.init(map: map) as? N { + object.mapping(map: map) + return object + } + } else if N.self is ImmutableMappable.Type { // Check if object is ImmutableMappable + assert(false, "'ImmutableMappable' type requires throwing version of function \(#function) - use 'try' before \(#function)") + } else { + // Ensure BaseMappable is not implemented directly + assert(false, "BaseMappable should not be implemented directly. Please implement Mappable, StaticMappable or ImmutableMappable") + } + + return nil + } + + // MARK: Mapping functions for Arrays and Dictionaries + + /// Maps a JSON array to an object that conforms to Mappable + public func mapArray(JSONString: String) -> [N]? { + let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) + + if let objectArray = mapArray(JSONObject: parsedJSON) { + return objectArray + } + + // failed to parse JSON into array form + // try to parse it into a dictionary and then wrap it in an array + if let object = map(JSONObject: parsedJSON) { + return [object] + } + + return nil + } + + /// Maps a JSON object to an array of Mappable objects if it is an array of JSON dictionary, or returns nil. + public func mapArray(JSONObject: Any?) -> [N]? { + if let JSONArray = JSONObject as? [[String: Any]] { + return mapArray(JSONArray: JSONArray) + } + + return nil + } + + /// Maps an array of JSON dictionary to an array of Mappable objects + public func mapArray(JSONArray: [[String: Any]]) -> [N]? { + // map every element in JSON array to type N + let result = JSONArray.flatMap(map) + return result + } + + /// Maps a JSON object to a dictionary of Mappable objects if it is a JSON dictionary of dictionaries, or returns nil. + public func mapDictionary(JSONString: String) -> [String: N]? { + let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) + return mapDictionary(JSONObject: parsedJSON) + } + + /// Maps a JSON object to a dictionary of Mappable objects if it is a JSON dictionary of dictionaries, or returns nil. + public func mapDictionary(JSONObject: Any?) -> [String: N]? { + if let JSON = JSONObject as? [String: [String: Any]] { + return mapDictionary(JSON: JSON) + } + + return nil + } + + /// Maps a JSON dictionary of dictionaries to a dictionary of Mappable objects + public func mapDictionary(JSON: [String: [String: Any]]) -> [String: N]? { + // map every value in dictionary to type N + let result = JSON.filterMap(map) + if result.isEmpty == false { + return result + } + + return nil + } + + /// Maps a JSON object to a dictionary of Mappable objects if it is a JSON dictionary of dictionaries, or returns nil. + public func mapDictionary(JSONObject: Any?, toDictionary dictionary: [String: N]) -> [String: N] { + if let JSON = JSONObject as? [String : [String : Any]] { + return mapDictionary(JSON: JSON, toDictionary: dictionary) + } + + return dictionary + } + + /// Maps a JSON dictionary of dictionaries to an existing dictionary of Mappable objects + public func mapDictionary(JSON: [String: [String: Any]], toDictionary dictionary: [String: N]) -> [String: N] { + var mutableDictionary = dictionary + for (key, value) in JSON { + if let object = dictionary[key] { + _ = map(JSON: value, toObject: object) + } else { + mutableDictionary[key] = map(JSON: value) + } + } + + return mutableDictionary + } + + /// Maps a JSON object to a dictionary of arrays of Mappable objects + public func mapDictionaryOfArrays(JSONObject: Any?) -> [String: [N]]? { + if let JSON = JSONObject as? [String: [[String: Any]]] { + return mapDictionaryOfArrays(JSON: JSON) + } + + return nil + } + + ///Maps a JSON dictionary of arrays to a dictionary of arrays of Mappable objects + public func mapDictionaryOfArrays(JSON: [String: [[String: Any]]]) -> [String: [N]]? { + // map every value in dictionary to type N + let result = JSON.filterMap { + mapArray(JSONArray: $0) + } + + if result.isEmpty == false { + return result + } + + return nil + } + + /// Maps an 2 dimentional array of JSON dictionaries to a 2 dimentional array of Mappable objects + public func mapArrayOfArrays(JSONObject: Any?) -> [[N]]? { + if let JSONArray = JSONObject as? [[[String: Any]]] { + var objectArray = [[N]]() + for innerJSONArray in JSONArray { + if let array = mapArray(JSONArray: innerJSONArray){ + objectArray.append(array) + } + } + + if objectArray.isEmpty == false { + return objectArray + } + } + + return nil + } + + // MARK: Utility functions for converting strings to JSON objects + + /// Convert a JSON String into a Dictionary using NSJSONSerialization + public static func parseJSONStringIntoDictionary(JSONString: String) -> [String: Any]? { + let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) + return parsedJSON as? [String: Any] + } + + /// Convert a JSON String into an Object using NSJSONSerialization + public static func parseJSONString(JSONString: String) -> Any? { + let data = JSONString.data(using: String.Encoding.utf8, allowLossyConversion: true) + if let data = data { + let parsedJSON: Any? + do { + parsedJSON = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) + } catch let error { + print(error) + parsedJSON = nil + } + return parsedJSON + } + + return nil + } +} + +extension Mapper { + + // MARK: Functions that create JSON from objects + + ///Maps an object that conforms to Mappable to a JSON dictionary + public func toJSON(_ object: N) -> [String: Any] { + var mutableObject = object + let map = Map(mappingType: .toJSON, JSON: [:], context: context, shouldIncludeNilValues: shouldIncludeNilValues) + mutableObject.mapping(map: map) + return map.JSON + } + + ///Maps an array of Objects to an array of JSON dictionaries [[String: Any]] + public func toJSONArray(_ array: [N]) -> [[String: Any]] { + return array.map { + // convert every element in array to JSON dictionary equivalent + self.toJSON($0) + } + } + + ///Maps a dictionary of Objects that conform to Mappable to a JSON dictionary of dictionaries. + public func toJSONDictionary(_ dictionary: [String: N]) -> [String: [String: Any]] { + return dictionary.map { k, v in + // convert every value in dictionary to its JSON dictionary equivalent + return (k, self.toJSON(v)) + } + } + + ///Maps a dictionary of Objects that conform to Mappable to a JSON dictionary of dictionaries. + public func toJSONDictionaryOfArrays(_ dictionary: [String: [N]]) -> [String: [[String: Any]]] { + return dictionary.map { k, v in + // convert every value (array) in dictionary to its JSON dictionary equivalent + return (k, self.toJSONArray(v)) + } + } + + /// Maps an Object to a JSON string with option of pretty formatting + public func toJSONString(_ object: N, prettyPrint: Bool = false) -> String? { + let JSONDict = toJSON(object) + + return Mapper.toJSONString(JSONDict as Any, prettyPrint: prettyPrint) + } + + /// Maps an array of Objects to a JSON string with option of pretty formatting + public func toJSONString(_ array: [N], prettyPrint: Bool = false) -> String? { + let JSONDict = toJSONArray(array) + + return Mapper.toJSONString(JSONDict as Any, prettyPrint: prettyPrint) + } + + /// Converts an Object to a JSON string with option of pretty formatting + public static func toJSONString(_ JSONObject: Any, prettyPrint: Bool) -> String? { + let options: JSONSerialization.WritingOptions = prettyPrint ? .prettyPrinted : [] + if let JSON = Mapper.toJSONData(JSONObject, options: options) { + return String(data: JSON, encoding: String.Encoding.utf8) + } + + return nil + } + + /// Converts an Object to JSON data with options + public static func toJSONData(_ JSONObject: Any, options: JSONSerialization.WritingOptions) -> Data? { + if JSONSerialization.isValidJSONObject(JSONObject) { + let JSONData: Data? + do { + JSONData = try JSONSerialization.data(withJSONObject: JSONObject, options: options) + } catch let error { + print(error) + JSONData = nil + } + + return JSONData + } + + return nil + } +} + +extension Mapper where N: Hashable { + + /// Maps a JSON array to an object that conforms to Mappable + public func mapSet(JSONString: String) -> Set? { + let parsedJSON: Any? = Mapper.parseJSONString(JSONString: JSONString) + + if let objectArray = mapArray(JSONObject: parsedJSON) { + return Set(objectArray) + } + + // failed to parse JSON into array form + // try to parse it into a dictionary and then wrap it in an array + if let object = map(JSONObject: parsedJSON) { + return Set([object]) + } + + return nil + } + + /// Maps a JSON object to an Set of Mappable objects if it is an array of JSON dictionary, or returns nil. + public func mapSet(JSONObject: Any?) -> Set? { + if let JSONArray = JSONObject as? [[String: Any]] { + return mapSet(JSONArray: JSONArray) + } + + return nil + } + + /// Maps an Set of JSON dictionary to an array of Mappable objects + public func mapSet(JSONArray: [[String: Any]]) -> Set { + // map every element in JSON array to type N + return Set(JSONArray.flatMap(map)) + } + + ///Maps a Set of Objects to a Set of JSON dictionaries [[String : Any]] + public func toJSONSet(_ set: Set) -> [[String: Any]] { + return set.map { + // convert every element in set to JSON dictionary equivalent + self.toJSON($0) + } + } + + /// Maps a set of Objects to a JSON string with option of pretty formatting + public func toJSONString(_ set: Set, prettyPrint: Bool = false) -> String? { + let JSONDict = toJSONSet(set) + + return Mapper.toJSONString(JSONDict as Any, prettyPrint: prettyPrint) + } +} + +extension Dictionary { + internal func map(_ f: (Element) throws -> (K, V)) rethrows -> [K: V] { + var mapped = [K: V]() + + for element in self { + let newElement = try f(element) + mapped[newElement.0] = newElement.1 + } + + return mapped + } + + internal func map(_ f: (Element) throws -> (K, [V])) rethrows -> [K: [V]] { + var mapped = [K: [V]]() + + for element in self { + let newElement = try f(element) + mapped[newElement.0] = newElement.1 + } + + return mapped + } + + + internal func filterMap(_ f: (Value) throws -> U?) rethrows -> [Key: U] { + var mapped = [Key: U]() + + for (key, value) in self { + if let newValue = try f(value) { + mapped[key] = newValue + } + } + + return mapped + } +} diff --git a/Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift b/Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift new file mode 100644 index 0000000..3b3ebab --- /dev/null +++ b/Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift @@ -0,0 +1,51 @@ +// +// TransformOf.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 8/22/16. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class NSDecimalNumberTransform: TransformType { + public typealias Object = NSDecimalNumber + public typealias JSON = String + + public init() {} + + open func transformFromJSON(_ value: Any?) -> NSDecimalNumber? { + if let string = value as? String { + return NSDecimalNumber(string: string) + } + if let double = value as? Double { + return NSDecimalNumber(value: double) + } + return nil + } + + open func transformToJSON(_ value: NSDecimalNumber?) -> String? { + guard let value = value else { return nil } + return value.description + } +} diff --git a/Pods/ObjectMapper/Sources/Operators.swift b/Pods/ObjectMapper/Sources/Operators.swift new file mode 100755 index 0000000..57741f8 --- /dev/null +++ b/Pods/ObjectMapper/Sources/Operators.swift @@ -0,0 +1,377 @@ +// +// Operators.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2014-10-09. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/** +* This file defines a new operator which is used to create a mapping between an object and a JSON key value. +* There is an overloaded operator definition for each type of object that is supported in ObjectMapper. +* This provides a way to add custom logic to handle specific types of objects +*/ + +/// Operator used for defining mappings to and from JSON +infix operator <- + +/// Operator used to define mappings to JSON +infix operator >>> + +// MARK:- Objects with Basic types + +/// Object of Basic type +public func <- (left: inout T, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.basicType(&left, object: right.value()) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: T, right: Map) { + if right.mappingType == .toJSON { + ToJSON.basicType(left, map: right) + } +} + + +/// Optional object of basic type +public func <- (left: inout T?, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalBasicType(&left, object: right.value()) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: T?, right: Map) { + if right.mappingType == .toJSON { + ToJSON.optionalBasicType(left, map: right) + } +} + + +/// Implicitly unwrapped optional object of basic type +public func <- (left: inout T!, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalBasicType(&left, object: right.value()) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Mappable Objects - + +/// Object conforming to Mappable +public func <- (left: inout T, right: Map) { + switch right.mappingType { + case .fromJSON: + FromJSON.object(&left, map: right) + case .toJSON: + left >>> right + } +} + +public func >>> (left: T, right: Map) { + if right.mappingType == .toJSON { + ToJSON.object(left, map: right) + } +} + + +/// Optional Mappable objects +public func <- (left: inout T?, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObject(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: T?, right: Map) { + if right.mappingType == .toJSON { + ToJSON.optionalObject(left, map: right) + } +} + + +/// Implicitly unwrapped optional Mappable objects +public func <- (left: inout T!, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObject(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Dictionary of Mappable objects - Dictionary + +/// Dictionary of Mappable objects +public func <- (left: inout Dictionary, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.objectDictionary(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Dictionary, right: Map) { + if right.mappingType == .toJSON { + ToJSON.objectDictionary(left, map: right) + } +} + + +/// Optional Dictionary of Mappable object +public func <- (left: inout Dictionary?, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectDictionary(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Dictionary?, right: Map) { + if right.mappingType == .toJSON { + ToJSON.optionalObjectDictionary(left, map: right) + } +} + + +/// Implicitly unwrapped Optional Dictionary of Mappable object +public func <- (left: inout Dictionary!, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectDictionary(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +/// Dictionary of Mappable objects +public func <- (left: inout Dictionary, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.objectDictionaryOfArrays(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Dictionary, right: Map) { + if right.mappingType == .toJSON { + ToJSON.objectDictionaryOfArrays(left, map: right) + } +} + +/// Optional Dictionary of Mappable object +public func <- (left: inout Dictionary?, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectDictionaryOfArrays(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Dictionary?, right: Map) { + if right.mappingType == .toJSON { + ToJSON.optionalObjectDictionaryOfArrays(left, map: right) + } +} + + +/// Implicitly unwrapped Optional Dictionary of Mappable object +public func <- (left: inout Dictionary!, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectDictionaryOfArrays(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Array of Mappable objects - Array + +/// Array of Mappable objects +public func <- (left: inout Array, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.objectArray(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Array, right: Map) { + if right.mappingType == .toJSON { + ToJSON.objectArray(left, map: right) + } +} + +/// Optional array of Mappable objects +public func <- (left: inout Array?, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectArray(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Array?, right: Map) { + if right.mappingType == .toJSON { + ToJSON.optionalObjectArray(left, map: right) + } +} + + +/// Implicitly unwrapped Optional array of Mappable objects +public func <- (left: inout Array!, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectArray(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Array of Array of Mappable objects - Array> + +/// Array of Array Mappable objects +public func <- (left: inout Array>, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.twoDimensionalObjectArray(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Array>, right: Map) { + if right.mappingType == .toJSON { + ToJSON.twoDimensionalObjectArray(left, map: right) + } +} + + +/// Optional array of Mappable objects +public func <- (left:inout Array>?, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalTwoDimensionalObjectArray(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Array>?, right: Map) { + if right.mappingType == .toJSON { + ToJSON.optionalTwoDimensionalObjectArray(left, map: right) + } +} + + +/// Implicitly unwrapped Optional array of Mappable objects +public func <- (left: inout Array>!, right: Map) { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalTwoDimensionalObjectArray(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Set of Mappable objects - Set + +/// Set of Mappable objects +public func <- (left: inout Set, right: Map) where T: Hashable { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.objectSet(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Set, right: Map) where T: Hashable { + if right.mappingType == .toJSON { + ToJSON.objectSet(left, map: right) + } +} + + +/// Optional Set of Mappable objects +public func <- (left: inout Set?, right: Map) where T: Hashable, T: Hashable { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectSet(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Set?, right: Map) where T: Hashable, T: Hashable { + if right.mappingType == .toJSON { + ToJSON.optionalObjectSet(left, map: right) + } +} + + +/// Implicitly unwrapped Optional Set of Mappable objects +public func <- (left: inout Set!, right: Map) where T: Hashable { + switch right.mappingType { + case .fromJSON where right.isKeyPresent: + FromJSON.optionalObjectSet(&left, map: right) + case .toJSON: + left >>> right + default: () + } +} diff --git a/Pods/ObjectMapper/Sources/ToJSON.swift b/Pods/ObjectMapper/Sources/ToJSON.swift new file mode 100644 index 0000000..311d1d5 --- /dev/null +++ b/Pods/ObjectMapper/Sources/ToJSON.swift @@ -0,0 +1,179 @@ +// +// ToJSON.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2014-10-13. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +private func setValue(_ value: Any, map: Map) { + setValue(value, key: map.currentKey!, checkForNestedKeys: map.keyIsNested, delimiter: map.nestedKeyDelimiter, dictionary: &map.JSON) +} + +private func setValue(_ value: Any, key: String, checkForNestedKeys: Bool, delimiter: String, dictionary: inout [String : Any]) { + if checkForNestedKeys { + let keyComponents = ArraySlice(key.components(separatedBy: delimiter).filter { !$0.isEmpty }.map { $0.characters }) + setValue(value, forKeyPathComponents: keyComponents, dictionary: &dictionary) + } else { + dictionary[key] = value + } +} + +private func setValue(_ value: Any, forKeyPathComponents components: ArraySlice, dictionary: inout [String : Any]) { + if components.isEmpty { + return + } + + let head = components.first! + + if components.count == 1 { + dictionary[String(head)] = value + } else { + var child = dictionary[String(head)] as? [String : Any] + if child == nil { + child = [:] + } + + let tail = components.dropFirst() + setValue(value, forKeyPathComponents: tail, dictionary: &child!) + + dictionary[String(head)] = child + } +} + +internal final class ToJSON { + + class func basicType(_ field: N, map: Map) { + if let x = field as Any? , false + || x is NSNumber // Basic types + || x is Bool + || x is Int + || x is Double + || x is Float + || x is String + || x is NSNull + || x is Array // Arrays + || x is Array + || x is Array + || x is Array + || x is Array + || x is Array + || x is Array + || x is Array> + || x is Dictionary // Dictionaries + || x is Dictionary + || x is Dictionary + || x is Dictionary + || x is Dictionary + || x is Dictionary + || x is Dictionary + { + setValue(x, map: map) + } + } + + class func optionalBasicType(_ field: N?, map: Map) { + if let field = field { + basicType(field, map: map) + } else if map.shouldIncludeNilValues { + basicType(NSNull(), map: map) //If BasicType is nil, emil NSNull into the JSON output + } + } + + class func object(_ field: N, map: Map) { + if let result = Mapper(context: map.context).toJSON(field) as Any? { + setValue(result, map: map) + } + } + + class func optionalObject(_ field: N?, map: Map) { + if let field = field { + object(field, map: map) + } + } + + class func objectArray(_ field: Array, map: Map) { + let JSONObjects = Mapper(context: map.context).toJSONArray(field) + + setValue(JSONObjects, map: map) + } + + class func optionalObjectArray(_ field: Array?, map: Map) { + if let field = field { + objectArray(field, map: map) + } + } + + class func twoDimensionalObjectArray(_ field: Array>, map: Map) { + var array = [[[String: Any]]]() + for innerArray in field { + let JSONObjects = Mapper(context: map.context).toJSONArray(innerArray) + array.append(JSONObjects) + } + setValue(array, map: map) + } + + class func optionalTwoDimensionalObjectArray(_ field: Array>?, map: Map) { + if let field = field { + twoDimensionalObjectArray(field, map: map) + } + } + + class func objectSet(_ field: Set, map: Map) where N: Hashable { + let JSONObjects = Mapper(context: map.context).toJSONSet(field) + + setValue(JSONObjects, map: map) + } + + class func optionalObjectSet(_ field: Set?, map: Map) where N: Hashable { + if let field = field { + objectSet(field, map: map) + } + } + + class func objectDictionary(_ field: Dictionary, map: Map) { + let JSONObjects = Mapper(context: map.context).toJSONDictionary(field) + + setValue(JSONObjects, map: map) + } + + class func optionalObjectDictionary(_ field: Dictionary?, map: Map) { + if let field = field { + objectDictionary(field, map: map) + } + } + + class func objectDictionaryOfArrays(_ field: Dictionary, map: Map) { + let JSONObjects = Mapper(context: map.context).toJSONDictionaryOfArrays(field) + + setValue(JSONObjects, map: map) + } + + class func optionalObjectDictionaryOfArrays(_ field: Dictionary?, map: Map) { + if let field = field { + objectDictionaryOfArrays(field, map: map) + } + } +} diff --git a/Pods/ObjectMapper/Sources/TransformOf.swift b/Pods/ObjectMapper/Sources/TransformOf.swift new file mode 100644 index 0000000..97056c6 --- /dev/null +++ b/Pods/ObjectMapper/Sources/TransformOf.swift @@ -0,0 +1,48 @@ +// +// TransformOf.swift +// ObjectMapper +// +// Created by Syo Ikeda on 1/23/15. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +open class TransformOf: TransformType { + public typealias Object = ObjectType + public typealias JSON = JSONType + + private let fromJSON: (JSONType?) -> ObjectType? + private let toJSON: (ObjectType?) -> JSONType? + + public init(fromJSON: @escaping(JSONType?) -> ObjectType?, toJSON: @escaping(ObjectType?) -> JSONType?) { + self.fromJSON = fromJSON + self.toJSON = toJSON + } + + open func transformFromJSON(_ value: Any?) -> ObjectType? { + return fromJSON(value as? JSONType) + } + + open func transformToJSON(_ value: ObjectType?) -> JSONType? { + return toJSON(value) + } +} diff --git a/Pods/ObjectMapper/Sources/TransformOperators.swift b/Pods/ObjectMapper/Sources/TransformOperators.swift new file mode 100644 index 0000000..dbe5ad2 --- /dev/null +++ b/Pods/ObjectMapper/Sources/TransformOperators.swift @@ -0,0 +1,606 @@ +// +// TransformOperators.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2016-09-26. +// Copyright © 2016 hearst. All rights reserved. +// + +import Foundation + +// MARK:- Transforms + +/// Object of Basic type with Transform +public func <- (left: inout Transform.Object, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let value = transform.transformFromJSON(map.currentValue) + FromJSON.basicType(&left, object: value) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Transform.Object, right: (Map, Transform)) { + let (map, transform) = right + if map.mappingType == .toJSON { + let value: Transform.JSON? = transform.transformToJSON(left) + ToJSON.optionalBasicType(value, map: map) + } +} + + +/// Optional object of basic type with Transform +public func <- (left: inout Transform.Object?, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let value = transform.transformFromJSON(map.currentValue) + FromJSON.optionalBasicType(&left, object: value) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Transform.Object?, right: (Map, Transform)) { + let (map, transform) = right + if map.mappingType == .toJSON { + let value: Transform.JSON? = transform.transformToJSON(left) + ToJSON.optionalBasicType(value, map: map) + } +} + + +/// Implicitly unwrapped optional object of basic type with Transform +public func <- (left: inout Transform.Object!, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let value = transform.transformFromJSON(map.currentValue) + FromJSON.optionalBasicType(&left, object: value) + case .toJSON: + left >>> right + default: () + } +} + +/// Array of Basic type with Transform +public func <- (left: inout [Transform.Object], right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let values = fromJSONArrayWithTransform(map.currentValue, transform: transform) + FromJSON.basicType(&left, object: values) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: [Transform.Object], right: (Map, Transform)) { + let (map, transform) = right + if map.mappingType == .toJSON{ + let values = toJSONArrayWithTransform(left, transform: transform) + ToJSON.optionalBasicType(values, map: map) + } +} + + +/// Optional array of Basic type with Transform +public func <- (left: inout [Transform.Object]?, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let values = fromJSONArrayWithTransform(map.currentValue, transform: transform) + FromJSON.optionalBasicType(&left, object: values) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: [Transform.Object]?, right: (Map, Transform)) { + let (map, transform) = right + if map.mappingType == .toJSON { + let values = toJSONArrayWithTransform(left, transform: transform) + ToJSON.optionalBasicType(values, map: map) + } +} + + +/// Implicitly unwrapped optional array of Basic type with Transform +public func <- (left: inout [Transform.Object]!, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let values = fromJSONArrayWithTransform(map.currentValue, transform: transform) + FromJSON.optionalBasicType(&left, object: values) + case .toJSON: + left >>> right + default: () + } +} + +/// Dictionary of Basic type with Transform +public func <- (left: inout [String: Transform.Object], right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let values = fromJSONDictionaryWithTransform(map.currentValue, transform: transform) + FromJSON.basicType(&left, object: values) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: [String: Transform.Object], right: (Map, Transform)) { + let (map, transform) = right + if map.mappingType == . toJSON { + let values = toJSONDictionaryWithTransform(left, transform: transform) + ToJSON.optionalBasicType(values, map: map) + } +} + + +/// Optional dictionary of Basic type with Transform +public func <- (left: inout [String: Transform.Object]?, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let values = fromJSONDictionaryWithTransform(map.currentValue, transform: transform) + FromJSON.optionalBasicType(&left, object: values) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: [String: Transform.Object]?, right: (Map, Transform)) { + let (map, transform) = right + if map.mappingType == .toJSON { + let values = toJSONDictionaryWithTransform(left, transform: transform) + ToJSON.optionalBasicType(values, map: map) + } +} + + +/// Implicitly unwrapped optional dictionary of Basic type with Transform +public func <- (left: inout [String: Transform.Object]!, right: (Map, Transform)) { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let values = fromJSONDictionaryWithTransform(map.currentValue, transform: transform) + FromJSON.optionalBasicType(&left, object: values) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Transforms of Mappable Objects - + +/// Object conforming to Mappable that have transforms +public func <- (left: inout Transform.Object, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let value: Transform.Object? = transform.transformFromJSON(map.currentValue) + FromJSON.basicType(&left, object: value) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Transform.Object, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let value: Transform.JSON? = transform.transformToJSON(left) + ToJSON.optionalBasicType(value, map: map) + } +} + + +/// Optional Mappable objects that have transforms +public func <- (left: inout Transform.Object?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let value: Transform.Object? = transform.transformFromJSON(map.currentValue) + FromJSON.optionalBasicType(&left, object: value) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Transform.Object?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON{ + let value: Transform.JSON? = transform.transformToJSON(left) + ToJSON.optionalBasicType(value, map: map) + } +} + + +/// Implicitly unwrapped optional Mappable objects that have transforms +public func <- (left: inout Transform.Object!, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let value: Transform.Object? = transform.transformFromJSON(map.currentValue) + FromJSON.optionalBasicType(&left, object: value) + case .toJSON: + left >>> right + default: () + } +} + + +// MARK:- Dictionary of Mappable objects with a transform - Dictionary + +/// Dictionary of Mappable objects with a transform +public func <- (left: inout Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .fromJSON && map.isKeyPresent, + let object = map.currentValue as? [String: AnyObject] { + let value = fromJSONDictionaryWithTransform(object as AnyObject?, transform: transform) ?? left + FromJSON.basicType(&left, object: value) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +public func >>> (left: Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let value = toJSONDictionaryWithTransform(left, transform: transform) + ToJSON.basicType(value, map: map) + } +} + + +/// Optional Dictionary of Mappable object with a transform +public func <- (left: inout Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .fromJSON && map.isKeyPresent, let object = map.currentValue as? [String : AnyObject]{ + let value = fromJSONDictionaryWithTransform(object as AnyObject?, transform: transform) ?? left + FromJSON.optionalBasicType(&left, object: value) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +public func >>> (left: Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let value = toJSONDictionaryWithTransform(left, transform: transform) + ToJSON.optionalBasicType(value, map: map) + } +} + + +/// Implicitly unwrapped Optional Dictionary of Mappable object with a transform +public func <- (left: inout Dictionary!, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .fromJSON && map.isKeyPresent, let dictionary = map.currentValue as? [String : AnyObject]{ + let transformedDictionary = fromJSONDictionaryWithTransform(dictionary as AnyObject?, transform: transform) ?? left + FromJSON.optionalBasicType(&left, object: transformedDictionary) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +/// Dictionary of Mappable objects with a transform +public func <- (left: inout Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + + if let dictionary = map.currentValue as? [String : [AnyObject]], map.mappingType == .fromJSON && map.isKeyPresent { + let transformedDictionary = dictionary.map { (key: String, values: [AnyObject]) -> (String, [Transform.Object]) in + if let jsonArray = fromJSONArrayWithTransform(values, transform: transform) { + return (key, jsonArray) + } + if let leftValue = left[key] { + return (key, leftValue) + } + return (key, []) + } + + FromJSON.basicType(&left, object: transformedDictionary) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +public func >>> (left: Dictionary, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + + if map.mappingType == .toJSON { + let transformedDictionary = left.map { (key, values) in + return (key, toJSONArrayWithTransform(values, transform: transform) ?? []) + } + + ToJSON.basicType(transformedDictionary, map: map) + } +} + + +/// Optional Dictionary of Mappable object with a transform +public func <- (left: inout Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + + if let dictionary = map.currentValue as? [String : [AnyObject]], map.mappingType == .fromJSON && map.isKeyPresent { + + let transformedDictionary = dictionary.map { (key: String, values: [AnyObject]) -> (String, [Transform.Object]) in + if let jsonArray = fromJSONArrayWithTransform(values, transform: transform) { + return (key, jsonArray) + } + if let leftValue = left?[key] { + return (key, leftValue) + } + return (key, []) + + } + + FromJSON.optionalBasicType(&left, object: transformedDictionary) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +public func >>> (left: Dictionary?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + + if map.mappingType == .toJSON { + let transformedDictionary = left?.map { (key, values) in + return (key, toJSONArrayWithTransform(values, transform: transform) ?? []) + } + + ToJSON.optionalBasicType(transformedDictionary, map: map) + } +} + + +/// Implicitly unwrapped Optional Dictionary of Mappable object with a transform +public func <- (left: inout Dictionary!, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + + if let dictionary = map.currentValue as? [String : [AnyObject]], map.mappingType == .fromJSON && map.isKeyPresent { + let transformedDictionary = dictionary.map { (key: String, values: [AnyObject]) -> (String, [Transform.Object]) in + if let jsonArray = fromJSONArrayWithTransform(values, transform: transform) { + return (key, jsonArray) + } + if let leftValue = left?[key] { + return (key, leftValue) + } + return (key, []) + } + FromJSON.optionalBasicType(&left, object: transformedDictionary) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +// MARK:- Array of Mappable objects with transforms - Array + +/// Array of Mappable objects +public func <- (left: inout Array, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { + FromJSON.basicType(&left, object: transformedValues) + } + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Array, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let transformedValues = toJSONArrayWithTransform(left, transform: transform) + ToJSON.optionalBasicType(transformedValues, map: map) + } +} + + +/// Optional array of Mappable objects +public func <- (left: inout Array?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) + FromJSON.optionalBasicType(&left, object: transformedValues) + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Array?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let transformedValues = toJSONArrayWithTransform(left, transform: transform) + ToJSON.optionalBasicType(transformedValues, map: map) + } +} + + +/// Implicitly unwrapped Optional array of Mappable objects +public func <- (left: inout Array!, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) + FromJSON.optionalBasicType(&left, object: transformedValues) + case .toJSON: + left >>> right + default: () + } +} + +// MARK:- Array of Array of Mappable objects - Array>> with transforms + +/// Array of Array Mappable objects with transform +public func <- (left: inout Array>, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .fromJSON && map.isKeyPresent, let original2DArray = map.currentValue as? [[AnyObject]]{ + let transformed2DArray = original2DArray.flatMap { values in + fromJSONArrayWithTransform(values as AnyObject?, transform: transform) + } + FromJSON.basicType(&left, object: transformed2DArray) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +public func >>> (left: Array>, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let transformed2DArray = left.flatMap { values in + toJSONArrayWithTransform(values, transform: transform) + } + ToJSON.basicType(transformed2DArray, map: map) + } +} + + +/// Optional array of Mappable objects with transform +public func <- (left:inout Array>?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .fromJSON && map.isKeyPresent, let original2DArray = map.currentValue as? [[AnyObject]]{ + let transformed2DArray = original2DArray.flatMap { values in + fromJSONArrayWithTransform(values as AnyObject?, transform: transform) + } + FromJSON.optionalBasicType(&left, object: transformed2DArray) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +public func >>> (left: Array>?, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let transformed2DArray = left?.flatMap { values in + toJSONArrayWithTransform(values, transform: transform) + } + ToJSON.optionalBasicType(transformed2DArray, map: map) + } +} + + +/// Implicitly unwrapped Optional array of Mappable objects with transform +public func <- (left: inout Array>!, right: (Map, Transform)) where Transform.Object: BaseMappable { + let (map, transform) = right + if map.mappingType == .fromJSON && map.isKeyPresent, let original2DArray = map.currentValue as? [[AnyObject]] { + let transformed2DArray = original2DArray.flatMap { values in + fromJSONArrayWithTransform(values as AnyObject?, transform: transform) + } + FromJSON.optionalBasicType(&left, object: transformed2DArray) + } else if map.mappingType == .toJSON { + left >>> right + } +} + +// MARK:- Set of Mappable objects with a transform - Set + +/// Set of Mappable objects with transform +public func <- (left: inout Set, right: (Map, Transform)) where Transform.Object: Hashable & BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { + FromJSON.basicType(&left, object: Set(transformedValues)) + } + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Set, right: (Map, Transform)) where Transform.Object: Hashable & BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + let transformedValues = toJSONArrayWithTransform(Array(left), transform: transform) + ToJSON.optionalBasicType(transformedValues, map: map) + } +} + + +/// Optional Set of Mappable objects with transform +public func <- (left: inout Set?, right: (Map, Transform)) where Transform.Object: Hashable & BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { + FromJSON.basicType(&left, object: Set(transformedValues)) + } + case .toJSON: + left >>> right + default: () + } +} + +public func >>> (left: Set?, right: (Map, Transform)) where Transform.Object: Hashable & BaseMappable { + let (map, transform) = right + if map.mappingType == .toJSON { + if let values = left { + let transformedValues = toJSONArrayWithTransform(Array(values), transform: transform) + ToJSON.optionalBasicType(transformedValues, map: map) + } + } +} + + +/// Implicitly unwrapped Optional set of Mappable objects with transform +public func <- (left: inout Set!, right: (Map, Transform)) where Transform.Object: Hashable & BaseMappable { + let (map, transform) = right + switch map.mappingType { + case .fromJSON where map.isKeyPresent: + if let transformedValues = fromJSONArrayWithTransform(map.currentValue, transform: transform) { + FromJSON.basicType(&left, object: Set(transformedValues)) + } + case .toJSON: + left >>> right + default: () + } +} + + +private func fromJSONArrayWithTransform(_ input: Any?, transform: Transform) -> [Transform.Object]? { + if let values = input as? [AnyObject] { + return values.flatMap { value in + return transform.transformFromJSON(value) + } + } else { + return nil + } +} + +private func fromJSONDictionaryWithTransform(_ input: Any?, transform: Transform) -> [String: Transform.Object]? { + if let values = input as? [String: AnyObject] { + return values.filterMap { value in + return transform.transformFromJSON(value) + } + } else { + return nil + } +} + +private func toJSONArrayWithTransform(_ input: [Transform.Object]?, transform: Transform) -> [Transform.JSON]? { + return input?.flatMap { value in + return transform.transformToJSON(value) + } +} + +private func toJSONDictionaryWithTransform(_ input: [String: Transform.Object]?, transform: Transform) -> [String: Transform.JSON]? { + return input?.filterMap { value in + return transform.transformToJSON(value) + } +} diff --git a/Pods/ObjectMapper/Sources/TransformType.swift b/Pods/ObjectMapper/Sources/TransformType.swift new file mode 100644 index 0000000..61578c3 --- /dev/null +++ b/Pods/ObjectMapper/Sources/TransformType.swift @@ -0,0 +1,35 @@ +// +// TransformType.swift +// ObjectMapper +// +// Created by Syo Ikeda on 2/4/15. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +public protocol TransformType { + associatedtype Object + associatedtype JSON + + func transformFromJSON(_ value: Any?) -> Object? + func transformToJSON(_ value: Object?) -> JSON? +} diff --git a/Pods/ObjectMapper/Sources/URLTransform.swift b/Pods/ObjectMapper/Sources/URLTransform.swift new file mode 100644 index 0000000..4ef109f --- /dev/null +++ b/Pods/ObjectMapper/Sources/URLTransform.swift @@ -0,0 +1,65 @@ +// +// URLTransform.swift +// ObjectMapper +// +// Created by Tristan Himmelman on 2014-10-27. +// +// The MIT License (MIT) +// +// Copyright (c) 2014-2016 Hearst +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +open class URLTransform: TransformType { + public typealias Object = URL + public typealias JSON = String + private let shouldEncodeURLString: Bool + + /** + Initializes the URLTransform with an option to encode URL strings before converting them to an NSURL + - parameter shouldEncodeUrlString: when true (the default) the string is encoded before passing + to `NSURL(string:)` + - returns: an initialized transformer + */ + public init(shouldEncodeURLString: Bool = true) { + self.shouldEncodeURLString = shouldEncodeURLString + } + + open func transformFromJSON(_ value: Any?) -> URL? { + guard let URLString = value as? String else { return nil } + + if !shouldEncodeURLString { + return URL(string: URLString) + } + + guard let escapedURLString = URLString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else { + return nil + } + return URL(string: escapedURLString) + } + + open func transformToJSON(_ value: URL?) -> String? { + if let URL = value { + return URL.absoluteString + } + return nil + } +} diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..328458e --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1784 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 022BA98FB777CB61FCEEC1E18426A054 /* ToastWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91412C7E96D6197059C9691DBFB5223 /* ToastWindow.swift */; }; + 0358D17087455057141DE3FB27665852 /* Pods-App-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = CFB9B6F0F36ECB44627648DFF07978C2 /* Pods-App-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0B27203E9B160A390F5E17796DE86932 /* ObjectMapper-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6AE7229A5BBAD52A1C548A418E532AAD /* ObjectMapper-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E9CC28AC8E34FE8E1C87E933E04BC7A /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ECA4696212612940D834EC974AD6C52 /* ImageProcessor.swift */; }; + 1234D5A1AC0B7C352DB5562859D68EDC /* SwiftyJSON-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5701D976FE49EEBC5F934D8CBD3ADC4F /* SwiftyJSON-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 12EF25578B49A115595FDFCCF1B4EDD0 /* ISO8601DateTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7DEFDB704E47359290737774678D0E /* ISO8601DateTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 179523942E7D1E3EB210EF05E2C7AE79 /* Kingfisher.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DC769A17E31DA1E34AB7281B7E0D844 /* Kingfisher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1875A69887F3DB414751C0FE1B008307 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + 191223246695B518ECCF7206E518725B /* URLTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8341ED5432782584EEBFDC4D63AC2EB6 /* URLTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 1C782F5035E677BF10E972411015B275 /* SCLAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390D503FED120763343D04B8903A3637 /* SCLAlertView.swift */; }; + 1FCE40F1294F6F419261CE2124FAA1B8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + 24B00DE95ABD4CCEB5319E522259AC88 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6994D1565179C1B03368B8B9E69F0FD /* SwiftyJSON.swift */; }; + 250BACC429DD19E9D523CC55E4F55158 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + 2868717245A49E3E130B53CC1BE7C123 /* EnumOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58285F094FF4CA5273C1242638D6D98 /* EnumOperators.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 2E5E570FFC49EF98550401421A243C0F /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340EEE1949B0F8C2CA4E8B7CEFE298BE /* CacheSerializer.swift */; }; + 314B9E9F909985AD40B416A0CE4D8F51 /* DateFormatterTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CE05A98CF2B573BDBF51E1FE55675D5 /* DateFormatterTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 3168742F7D72DF80FA485C69BA3B9D90 /* Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280E0ED002649293BC65E5AB962A5C42 /* Mappable.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 332E04A8CC0927C79942013D9E7EEDF9 /* SwiftyAttributes-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5575B1EFE5F487F89620EE6EDDA8B3F2 /* SwiftyAttributes-dummy.m */; }; + 33395912F2FFA355C4C36B7EDEAA3DA7 /* ImageView+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBC3F63626429F7135744B2C33A89E2E /* ImageView+Kingfisher.swift */; }; + 33A14E817C0FA585D5DA3A55E453299D /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = E759F3003107DA8BACAC0224684852C9 /* Image.swift */; }; + 3414E1D9CB15149A296E16C45967B021 /* ImagePrefetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B0D191B94EB8AA1D1A8AA0DD5EDE9C /* ImagePrefetcher.swift */; }; + 342AE33215135AD56B73580012DC22AB /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA603788C28BF0BA02BD69CEFD0E933A /* AnimatedImageView.swift */; }; + 378B44CAA91C2106076668FDE72E1E02 /* KingfisherOptionsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A684757E050ABE8CA5F8BAA3D278DBF3 /* KingfisherOptionsInfo.swift */; }; + 3E481544A395D7DBD4677A764E36E49C /* SCLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F4728D4A15B3D0E9788EF2EB5E6B68 /* SCLExtensions.swift */; }; + 3EA3F0AC878080E9081A5ACD45EBC93C /* String+SwiftyAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE1BC7F28F04A50779F991E0DA6EFF4E /* String+SwiftyAttributes.swift */; }; + 4089E39F76F86466B8DDD39A1C1ABB6B /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7518259D580A169721D720B2E2C30C0 /* Filter.swift */; }; + 42F182CCC2C8B7521C2ABBD5D61B8F67 /* TransformOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 900298F507328C7282016C33D1F41F6B /* TransformOf.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4421476F7F26886F96E830C664C39FA3 /* ImmutableMappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC0DB1AE78988360B610A23EDEBAEB2D /* ImmutableMappable.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 45CA1F8F65C0E30B5402A998E15DEBB6 /* EnumTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC29AC28A057D9B3E17B259B41BDAE01 /* EnumTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4BDEF50B66EF9FE4AB936903B5605FC8 /* Attribute+Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA928CA7DFCF1CB55D26CDC1C144B58E /* Attribute+Sequence.swift */; }; + 4C86D4E914CE708EDEC9B00D9E32770C /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16970D610E9E01EF4B23E2E3FCEB23AB /* Map.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4C8E3610E9DEE3F3EF326AEE6C104DB5 /* MapError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAFEF364B8446A2262A74E2C9F7B8FA /* MapError.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4C9B4B9B7D3C32DF1C06A37502D4950D /* TextEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0A17E8698564E0CA4C5177D1D6A15D /* TextEffect.swift */; }; + 4DFCF4DAA9D6E29E454F60FD792808E3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + 4FC991960B752A636DAB258F0E7A5D08 /* NSMutableAttributedString+SwiftyAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4BD01E9E340526FDB022B0AC660DB5 /* NSMutableAttributedString+SwiftyAttributes.swift */; }; + 4FDEABA45359B146DF34457AFD40CB35 /* DateTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491BE29441BA0CC5EFFB8F831470DB1D /* DateTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 50F243DC169000D26B57BF1EDB636EEA /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB1E4CB77C12BE4CFC21D6B7CF274364 /* Operators.swift */; }; + 52A280AB4852FDE92DF09B5E79D5C2AA /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BFB5C2202954F2262AB92C14993B8A /* Indicator.swift */; }; + 5BDD00BC7828B3B2D99C575577C82DCC /* SwiftyAttributes-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B321DA619965869FF11C12A069510605 /* SwiftyAttributes-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 61EDCBC66BA7717EE6FCDBDF4563082C /* SCLAlertView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D9279CA4C2057BA57D5A23229CB1CB73 /* SCLAlertView-dummy.m */; }; + 6A375593407BC7937234B2B6A0811760 /* Kingfisher-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D88417549C207E0C2BBCA6F4CEFFF6C /* Kingfisher-dummy.m */; }; + 6CD63E1564618B6213B98E0ABBC3127C /* ThreadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A37E90DC35456D87ECD8576ED2CA27FD /* ThreadHelper.swift */; }; + 6DA770087D354CDCD3889CDAE7C4C698 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79EC1DDFAD34B5791B4613CEEF8CF5C /* Box.swift */; }; + 6E29549E7FE6860985B2FF6FC82F0451 /* SwiftyJSON-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BECB5CD445868E338328202F763379D9 /* SwiftyJSON-dummy.m */; }; + 6F8FA966CB8A3AEF6F93ED7FFF58DB4F /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57B85CC6CE02A0A151F6AB7AAB3E871 /* ToastView.swift */; }; + 7183AD43047C455B682A4020359C2A5E /* CustomDateFormatTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F57B0F8F4C1B84A8CDC1C273BEEEB5 /* CustomDateFormatTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 7636F846675C9F4ED96E00C6C19A4890 /* KingfisherManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5910BE73A82353E1F6B92E7F80D93C /* KingfisherManager.swift */; }; + 78BE53AA86407BA8F49F4E3CB49870E7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83228F1C33EACB8B498EACE0A47D4E70 /* UIKit.framework */; }; + 7912101A1E1270B55E2D09DE41BAC29A /* Attribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EF2B97F288CBD06982F5670B01D356 /* Attribute.swift */; }; + 7D54B5190E71C28EFCACCD4C79F1D112 /* NSAttributedString+SwiftyAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F081FAD8410108B8DC76764B1D3F07E /* NSAttributedString+SwiftyAttributes.swift */; }; + 7DB2736C0961E9ACE1ECC680C121B5C7 /* WritingDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1683433091A07D620395F0C34A5E76C /* WritingDirection.swift */; }; + 7F1E9E1E1B49552F24FB32C65B9FEB7A /* ToJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6064D056B5B49B6DBF5E344216CAB4B1 /* ToJSON.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 86B7DA678C8705523C300947117C0706 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 707FFCAB7EBA4D017FB00BD8A9DF4C01 /* ImageTransition.swift */; }; + 8CEC01EF09ADC0B68F2F8FB26E49A3EC /* Ligatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4138194D6E12FA8809A2D09AAD682C /* Ligatures.swift */; }; + 8DA7E6883118CB3C8132B64D3D43AE0B /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3926BAABA75706BCFA2B11502C90BCA /* Resource.swift */; }; + 8F4E41DB8356A898FD939835FFA9D9B5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + 92136C6584624998D5005F46C70A628B /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8498F0254A5E78F9F55D8898559BF8 /* CFNetwork.framework */; }; + 9663A29E978A80886C5FA9865AEB220C /* NSDecimalNumberTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73F73B2A2B769D1D99A14E6E138D1B9 /* NSDecimalNumberTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 9C99E45B90BD95712D9878994BFC8B88 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + 9F826F893E86EB86591B637A1CE4B13A /* String+MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA710CB29F55225463221333CCA78D5A /* String+MD5.swift */; }; + A039D5F98F4C1621CDA454999C29F1C6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + A5014B98E0B339947198C6803DE634E8 /* DictionaryTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FD0A16D8FF099986844552811646D57 /* DictionaryTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + A621C1E0E3AE11E996301062239C4018 /* SwiftOverlays-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 61ECE3A023C33165A4A356F6D053013C /* SwiftOverlays-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6C2F066D75588425CC85D20985C2EE2 /* TransformType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B4F865BA04E00E88D0C5A5035411D7 /* TransformType.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + A7E5D232DC1007984ED91E0CFC80E496 /* VerticalGlyphForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F1502CBBBC8C06983666D5A8BADB80 /* VerticalGlyphForm.swift */; }; + A83C9438D9D5695F8E18FB652CA9F168 /* HexColorTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6719DBD92EE3C4A0C5E03745E62C675 /* HexColorTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + A8F35D1EBAEF99D38F02A0B8CA5D8FBE /* Kingfisher-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 025D1FF262144C849C82C96AB0F0853C /* Kingfisher-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ACF61BFE7A8F95270DF2C0AA6D7A221D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79690461CB9B6AFD42324392012C8FC9 /* QuartzCore.framework */; }; + B3F322D2C4B88A3EE24DA8797D1F3189 /* ObjectMapper-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 53FA93B929987B8CFC517E51EE330FDC /* ObjectMapper-dummy.m */; }; + B4C5619F183A5A456B541B0E737AA13B /* SwiftOverlays.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF4DF6A946EA6940303272FA0422F80 /* SwiftOverlays.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + BA1EE53EB078061EF3DE750109DBB170 /* SCLAlertView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A531FF0DFC411748F044F7DB009026C2 /* SCLAlertView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C13A63D03E1033AF9BF05013DE75CF65 /* SwiftOverlays-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF1FCBEA2CFD6AF0F728F87094A1C58 /* SwiftOverlays-dummy.m */; }; + C9CC5ED97D29C6C3DE22B78A9AA2AD91 /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180C502F645A5C61CD6623EF9E0DA8AA /* Kingfisher.swift */; }; + CBA1828775284D4B79D101BFC06B890E /* AttributeName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1886F3B95F12A912EF8F352468DA13C9 /* AttributeName.swift */; }; + D0A478F606AE848C8EA23D439F0E628C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */; }; + D3178EA66917B1D492F59DA298436ED7 /* Toaster-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CF6B7BA42D0E502485F3CEB980E7F4C /* Toaster-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4679FA67F8376FB88C226BA44A0E939 /* Pods-App-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C8144347BC58521E07DC398E6B01780 /* Pods-App-dummy.m */; }; + D6AC19ADE405294C55EA86A5251D92C2 /* Toaster-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CC933E7D65DBFA580335BDAA6065A3B /* Toaster-dummy.m */; }; + D899EDF480F3D18FCF8AC924ECF71BF6 /* Toaster.h in Headers */ = {isa = PBXBuildFile; fileRef = FEDE850FB850BA4E97A2DA9A53D520C1 /* Toaster.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DC1F12C360F2419DB390C24A7AC772D4 /* FromJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFF8B1591956A1E6621F95F7DA49D8A /* FromJSON.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + DC6080C64C269628C3F5107BE87055E2 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D380CFFDCCAACBCFC39601CD014F25F /* ImageDownloader.swift */; }; + E776A6BEB74DF1AEB7460F71894BF253 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCB19DEF359F965F3DF4D2ED08DB7CE /* Toast.swift */; }; + E9D0A1BD282C3D6E46D284644A676921 /* DataTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFADAB654BF8506E72366E190401BA55 /* DataTransform.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + EA816A580C3858934ED6FE92040C26DD /* RequestModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA237330DEC4CC6B1FE401275CC67ADA /* RequestModifier.swift */; }; + EE3A19C18674237A97F15FE09BE71EA3 /* Mapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7DA7769B172D024B0267D2A2069B69B /* Mapper.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + EE89512E8D36E67CDC525DB9D5BBF5A7 /* UIButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4195BEEFCCEF86C9EFD8459C31F4BD /* UIButton+Kingfisher.swift */; }; + F3AED763881AAEE63A7A927498C32ACC /* ToastCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3B453C1649585ADC1F7756ACBAAB3D /* ToastCenter.swift */; }; + F6EC7E4B2FA0CEF7F9A36AC4444B71DB /* TransformOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA8A51F7726B8101FE20CB1A54CFE00 /* TransformOperators.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + F8E957950566622A3E4B61AACE8F0B4A /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D40E9C4B0A394CAF0C5D6D9D98A7DF /* ImageCache.swift */; }; + FEEF32367907C0D129AD9DE56F6932D5 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF39590435847619C2F7AAAD9D44A4D2 /* Operators.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 274FFB0326D4DE33CF6CBDABA7FCEDFE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = B43918AB915242B851ABEFB5D70BE0AD; + remoteInfo = ObjectMapper; + }; + 49E86076679F7D3B63E967DA7DC2F3D6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = C4944FEC314D1A66588651D006273ADE; + remoteInfo = Kingfisher; + }; + 520A4997E95787B084FC97ADD8D0A7DC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = E951149A9EEDC6743F5EDBC1B869A3A3; + remoteInfo = SwiftyAttributes; + }; + 6A7EC7087197BFC36F32E6F684FBC679 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 17F8283B6318A06B3FE0121AB69E337E; + remoteInfo = SwiftOverlays; + }; + A2BD1E3D7B87DE6507BA0DF6BE473DB9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = ADCF391CDDFA64415DE4E0FEE7223817; + remoteInfo = Toaster; + }; + D2D8D7183AA7BC95B977D32693E4F2F9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = CDBB4A964696BB8EDEFFCAE8F72ED4F0; + remoteInfo = SCLAlertView; + }; + EB4188BC3B1B40F0A7B4D25C6A1F9244 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 27801029A78BC07E067F119CE3349CA6; + remoteInfo = SwiftyJSON; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 025D1FF262144C849C82C96AB0F0853C /* Kingfisher-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-umbrella.h"; sourceTree = ""; }; + 026462E74CBE6FB9F060C0ACEDDCFE46 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_App.framework; path = "Pods-App.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 055AA51A37EAA0C48CF46D2396778291 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Kingfisher.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 09D40E9C4B0A394CAF0C5D6D9D98A7DF /* ImageCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Sources/ImageCache.swift; sourceTree = ""; }; + 0C3B453C1649585ADC1F7756ACBAAB3D /* ToastCenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToastCenter.swift; path = Sources/ToastCenter.swift; sourceTree = ""; }; + 0D380CFFDCCAACBCFC39601CD014F25F /* ImageDownloader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageDownloader.swift; path = Sources/ImageDownloader.swift; sourceTree = ""; }; + 0D88417549C207E0C2BBCA6F4CEFFF6C /* Kingfisher-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Kingfisher-dummy.m"; sourceTree = ""; }; + 168DC307D781ED1E4AF0F390499BB905 /* SwiftyJSON.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyJSON.xcconfig; sourceTree = ""; }; + 16970D610E9E01EF4B23E2E3FCEB23AB /* Map.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Map.swift; path = Sources/Map.swift; sourceTree = ""; }; + 180C502F645A5C61CD6623EF9E0DA8AA /* Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Kingfisher.swift; path = Sources/Kingfisher.swift; sourceTree = ""; }; + 188406E777C0E9BDB827BC8C6A38363B /* ObjectMapper.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ObjectMapper.xcconfig; sourceTree = ""; }; + 1886F3B95F12A912EF8F352468DA13C9 /* AttributeName.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AttributeName.swift; path = SwiftyAttributes/Sources/common/AttributeName.swift; sourceTree = ""; }; + 1C26B04D8FB07ABC60CD51CF27FF0CF9 /* SwiftyAttributes.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftyAttributes.framework; path = SwiftyAttributes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1CBFE1F0D19F610E66A895D50B4D6E40 /* ObjectMapper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = ObjectMapper.framework; path = ObjectMapper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1CE05A98CF2B573BDBF51E1FE55675D5 /* DateFormatterTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateFormatterTransform.swift; path = Sources/DateFormatterTransform.swift; sourceTree = ""; }; + 1D6EA002525E00EB47878C00F75C6E12 /* Toaster-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Toaster-prefix.pch"; sourceTree = ""; }; + 1E4195BEEFCCEF86C9EFD8459C31F4BD /* UIButton+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIButton+Kingfisher.swift"; path = "Sources/UIButton+Kingfisher.swift"; sourceTree = ""; }; + 20F1502CBBBC8C06983666D5A8BADB80 /* VerticalGlyphForm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = VerticalGlyphForm.swift; path = SwiftyAttributes/Sources/common/VerticalGlyphForm.swift; sourceTree = ""; }; + 280E0ED002649293BC65E5AB962A5C42 /* Mappable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Mappable.swift; path = Sources/Mappable.swift; sourceTree = ""; }; + 2CF6B7BA42D0E502485F3CEB980E7F4C /* Toaster-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Toaster-umbrella.h"; sourceTree = ""; }; + 2ECA4696212612940D834EC974AD6C52 /* ImageProcessor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageProcessor.swift; path = Sources/ImageProcessor.swift; sourceTree = ""; }; + 2FD0A16D8FF099986844552811646D57 /* DictionaryTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DictionaryTransform.swift; path = Sources/DictionaryTransform.swift; sourceTree = ""; }; + 309BDAF707A8AD2B50EB2AF12220AB0E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 337EAD924FD67F5B4E29E4188F340512 /* Pods-App-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-App-acknowledgements.markdown"; sourceTree = ""; }; + 340EEE1949B0F8C2CA4E8B7CEFE298BE /* CacheSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CacheSerializer.swift; path = Sources/CacheSerializer.swift; sourceTree = ""; }; + 3420D5FAFC83638FB809D52289603DF1 /* SwiftOverlays.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = SwiftOverlays.modulemap; sourceTree = ""; }; + 34BFB5C2202954F2262AB92C14993B8A /* Indicator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Indicator.swift; path = Sources/Indicator.swift; sourceTree = ""; }; + 390D503FED120763343D04B8903A3637 /* SCLAlertView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SCLAlertView.swift; path = SCLAlertView/SCLAlertView.swift; sourceTree = ""; }; + 3D0504B01F2638FBA19B4FC160EE5B1B /* Pods-App-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-App-acknowledgements.plist"; sourceTree = ""; }; + 41170103A297A0F759E307D01F51384A /* SwiftOverlays-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftOverlays-prefix.pch"; sourceTree = ""; }; + 42AEFD34151B6F02C5D1E69B3CD61433 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 478D2A40B9F6CD2FA965CCD860EAFB49 /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-App.debug.xcconfig"; sourceTree = ""; }; + 491BE29441BA0CC5EFFB8F831470DB1D /* DateTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateTransform.swift; path = Sources/DateTransform.swift; sourceTree = ""; }; + 49DFA38C51AC6D425719179BB3442E57 /* SwiftyAttributes-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyAttributes-prefix.pch"; sourceTree = ""; }; + 4FAC16DF70DE22A85BBF55E089BB56E7 /* Pods-App.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-App.modulemap"; sourceTree = ""; }; + 4FC1BCA0CEB5F1BF08672ACFD5DF727E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50E5A1196EB41EFC077A3B6C8A17281D /* Kingfisher.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = Kingfisher.modulemap; sourceTree = ""; }; + 53FA93B929987B8CFC517E51EE330FDC /* ObjectMapper-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ObjectMapper-dummy.m"; sourceTree = ""; }; + 5575B1EFE5F487F89620EE6EDDA8B3F2 /* SwiftyAttributes-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyAttributes-dummy.m"; sourceTree = ""; }; + 5645958D70DCD5127C0053726750FD6A /* ObjectMapper.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = ObjectMapper.modulemap; sourceTree = ""; }; + 56B4F865BA04E00E88D0C5A5035411D7 /* TransformType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformType.swift; path = Sources/TransformType.swift; sourceTree = ""; }; + 5701D976FE49EEBC5F934D8CBD3ADC4F /* SwiftyJSON-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-umbrella.h"; sourceTree = ""; }; + 5C8144347BC58521E07DC398E6B01780 /* Pods-App-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-App-dummy.m"; sourceTree = ""; }; + 5CC933E7D65DBFA580335BDAA6065A3B /* Toaster-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Toaster-dummy.m"; sourceTree = ""; }; + 6064D056B5B49B6DBF5E344216CAB4B1 /* ToJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToJSON.swift; path = Sources/ToJSON.swift; sourceTree = ""; }; + 60D98A67B51133212D464D292DB8AD90 /* SwiftyJSON-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-prefix.pch"; sourceTree = ""; }; + 61ECE3A023C33165A4A356F6D053013C /* SwiftOverlays-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftOverlays-umbrella.h"; sourceTree = ""; }; + 6334E785167733FEBB6F54A43C3854EB /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-App.release.xcconfig"; sourceTree = ""; }; + 6549790AD76DA84F6E0B5B5C4194A42A /* SwiftOverlays.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftOverlays.xcconfig; sourceTree = ""; }; + 69F22F903747E5C0A1EF6C98A017744E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 69F57B0F8F4C1B84A8CDC1C273BEEEB5 /* CustomDateFormatTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CustomDateFormatTransform.swift; path = Sources/CustomDateFormatTransform.swift; sourceTree = ""; }; + 6AE7229A5BBAD52A1C548A418E532AAD /* ObjectMapper-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ObjectMapper-umbrella.h"; sourceTree = ""; }; + 6EECB0A3DE4E4CFD8F4D489851D461C0 /* SCLAlertView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SCLAlertView.xcconfig; sourceTree = ""; }; + 704449965FC05B7C43A7FEE564D45E4A /* Kingfisher-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-prefix.pch"; sourceTree = ""; }; + 707FFCAB7EBA4D017FB00BD8A9DF4C01 /* ImageTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageTransition.swift; path = Sources/ImageTransition.swift; sourceTree = ""; }; + 79690461CB9B6AFD42324392012C8FC9 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + 7DC769A17E31DA1E34AB7281B7E0D844 /* Kingfisher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Kingfisher.h; path = Sources/Kingfisher.h; sourceTree = ""; }; + 7EBD7B01DB6A7FA2190280D7CE39C5D4 /* SwiftOverlays.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftOverlays.framework; path = SwiftOverlays.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 83228F1C33EACB8B498EACE0A47D4E70 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 8341ED5432782584EEBFDC4D63AC2EB6 /* URLTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLTransform.swift; path = Sources/URLTransform.swift; sourceTree = ""; }; + 87F4728D4A15B3D0E9788EF2EB5E6B68 /* SCLExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SCLExtensions.swift; path = SCLAlertView/SCLExtensions.swift; sourceTree = ""; }; + 8B4B7698B8EC9934045037500FC9A07F /* SCLAlertView.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = SCLAlertView.modulemap; sourceTree = ""; }; + 8F081FAD8410108B8DC76764B1D3F07E /* NSAttributedString+SwiftyAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSAttributedString+SwiftyAttributes.swift"; path = "SwiftyAttributes/Sources/common/NSAttributedString+SwiftyAttributes.swift"; sourceTree = ""; }; + 900298F507328C7282016C33D1F41F6B /* TransformOf.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformOf.swift; path = Sources/TransformOf.swift; sourceTree = ""; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 94038376D370146BBE1F9C238F6BAF36 /* Pods-App-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-App-resources.sh"; sourceTree = ""; }; + 994D343D14F4A11A24B21065EBDD92E4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9A61AF76E37367E03E50E27A812699A9 /* Toaster.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = Toaster.modulemap; sourceTree = ""; }; + 9AFF8B1591956A1E6621F95F7DA49D8A /* FromJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FromJSON.swift; path = Sources/FromJSON.swift; sourceTree = ""; }; + 9BCB19DEF359F965F3DF4D2ED08DB7CE /* Toast.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = Sources/Toast.swift; sourceTree = ""; }; + 9EF6900B32D2A06A0EBEE4DB2950505B /* ObjectMapper-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ObjectMapper-prefix.pch"; sourceTree = ""; }; + 9F136E99B9546D10971B0BBF42A37AB8 /* Toaster.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Toaster.framework; path = Toaster.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A37E90DC35456D87ECD8576ED2CA27FD /* ThreadHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadHelper.swift; path = Sources/ThreadHelper.swift; sourceTree = ""; }; + A531FF0DFC411748F044F7DB009026C2 /* SCLAlertView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SCLAlertView-umbrella.h"; sourceTree = ""; }; + A684757E050ABE8CA5F8BAA3D278DBF3 /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherOptionsInfo.swift; path = Sources/KingfisherOptionsInfo.swift; sourceTree = ""; }; + A91412C7E96D6197059C9691DBFB5223 /* ToastWindow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToastWindow.swift; path = Sources/ToastWindow.swift; sourceTree = ""; }; + A9DECBB12CB7C070F203B7FD27B1A9CF /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AA237330DEC4CC6B1FE401275CC67ADA /* RequestModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestModifier.swift; path = Sources/RequestModifier.swift; sourceTree = ""; }; + AAF4DF6A946EA6940303272FA0422F80 /* SwiftOverlays.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftOverlays.swift; path = SwiftOverlays/SwiftOverlays.swift; sourceTree = ""; }; + AB2270341E9944D2771F0BC0377F8000 /* SwiftyJSON.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = SwiftyJSON.modulemap; sourceTree = ""; }; + ABA8A51F7726B8101FE20CB1A54CFE00 /* TransformOperators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformOperators.swift; path = Sources/TransformOperators.swift; sourceTree = ""; }; + AC0DB1AE78988360B610A23EDEBAEB2D /* ImmutableMappable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImmutableMappable.swift; path = Sources/ImmutableMappable.swift; sourceTree = ""; }; + ACC2678249D07007C9BF118E3EB3AA90 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AFADAB654BF8506E72366E190401BA55 /* DataTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataTransform.swift; path = Sources/DataTransform.swift; sourceTree = ""; }; + B321DA619965869FF11C12A069510605 /* SwiftyAttributes-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyAttributes-umbrella.h"; sourceTree = ""; }; + B4EF2B97F288CBD06982F5670B01D356 /* Attribute.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Attribute.swift; path = SwiftyAttributes/Sources/common/Attribute.swift; sourceTree = ""; }; + B58285F094FF4CA5273C1242638D6D98 /* EnumOperators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EnumOperators.swift; path = Sources/EnumOperators.swift; sourceTree = ""; }; + B5D2FAFC8B327CB70015BE9FED6214E0 /* SCLAlertView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SCLAlertView.framework; path = SCLAlertView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B6719DBD92EE3C4A0C5E03745E62C675 /* HexColorTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HexColorTransform.swift; path = Sources/HexColorTransform.swift; sourceTree = ""; }; + B6B418F8AEB6C257B47844EA27C423FC /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftyJSON.framework; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B7518259D580A169721D720B2E2C30C0 /* Filter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = Sources/Filter.swift; sourceTree = ""; }; + BA710CB29F55225463221333CCA78D5A /* String+MD5.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+MD5.swift"; path = "Sources/String+MD5.swift"; sourceTree = ""; }; + BB4F1350996DEA205A6A88B280642397 /* Toaster.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Toaster.xcconfig; sourceTree = ""; }; + BBC3F63626429F7135744B2C33A89E2E /* ImageView+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ImageView+Kingfisher.swift"; path = "Sources/ImageView+Kingfisher.swift"; sourceTree = ""; }; + BE1BC7F28F04A50779F991E0DA6EFF4E /* String+SwiftyAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+SwiftyAttributes.swift"; path = "SwiftyAttributes/Sources/common/String+SwiftyAttributes.swift"; sourceTree = ""; }; + BECB5CD445868E338328202F763379D9 /* SwiftyJSON-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyJSON-dummy.m"; sourceTree = ""; }; + BF39590435847619C2F7AAAD9D44A4D2 /* Operators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Sources/Operators.swift; sourceTree = ""; }; + C4B0D191B94EB8AA1D1A8AA0DD5EDE9C /* ImagePrefetcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImagePrefetcher.swift; path = Sources/ImagePrefetcher.swift; sourceTree = ""; }; + C6994D1565179C1B03368B8B9E69F0FD /* SwiftyJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = Source/SwiftyJSON.swift; sourceTree = ""; }; + C79EC1DDFAD34B5791B4613CEEF8CF5C /* Box.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Box.swift; sourceTree = ""; }; + CB0A17E8698564E0CA4C5177D1D6A15D /* TextEffect.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextEffect.swift; path = SwiftyAttributes/Sources/common/TextEffect.swift; sourceTree = ""; }; + CB4138194D6E12FA8809A2D09AAD682C /* Ligatures.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Ligatures.swift; path = SwiftyAttributes/Sources/common/Ligatures.swift; sourceTree = ""; }; + CC29AC28A057D9B3E17B259B41BDAE01 /* EnumTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EnumTransform.swift; path = Sources/EnumTransform.swift; sourceTree = ""; }; + CCD8EEF11AF208CB282FB69552083F94 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CE8498F0254A5E78F9F55D8898559BF8 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + CFB9B6F0F36ECB44627648DFF07978C2 /* Pods-App-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-App-umbrella.h"; sourceTree = ""; }; + D1683433091A07D620395F0C34A5E76C /* WritingDirection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WritingDirection.swift; path = SwiftyAttributes/Sources/common/WritingDirection.swift; sourceTree = ""; }; + D3926BAABA75706BCFA2B11502C90BCA /* Resource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Resource.swift; path = Sources/Resource.swift; sourceTree = ""; }; + D73F73B2A2B769D1D99A14E6E138D1B9 /* NSDecimalNumberTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NSDecimalNumberTransform.swift; path = Sources/NSDecimalNumberTransform.swift; sourceTree = ""; }; + D9279CA4C2057BA57D5A23229CB1CB73 /* SCLAlertView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SCLAlertView-dummy.m"; sourceTree = ""; }; + DA7DEFDB704E47359290737774678D0E /* ISO8601DateTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ISO8601DateTransform.swift; path = Sources/ISO8601DateTransform.swift; sourceTree = ""; }; + DAF1FCBEA2CFD6AF0F728F87094A1C58 /* SwiftOverlays-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftOverlays-dummy.m"; sourceTree = ""; }; + DBC74A2214EDE552725FB5B35CDB5D3B /* SCLAlertView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SCLAlertView-prefix.pch"; sourceTree = ""; }; + E57B85CC6CE02A0A151F6AB7AAB3E871 /* ToastView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToastView.swift; path = Sources/ToastView.swift; sourceTree = ""; }; + E759F3003107DA8BACAC0224684852C9 /* Image.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Image.swift; sourceTree = ""; }; + E7DA7769B172D024B0267D2A2069B69B /* Mapper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Mapper.swift; path = Sources/Mapper.swift; sourceTree = ""; }; + EA5910BE73A82353E1F6B92E7F80D93C /* KingfisherManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherManager.swift; path = Sources/KingfisherManager.swift; sourceTree = ""; }; + EA928CA7DFCF1CB55D26CDC1C144B58E /* Attribute+Sequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Attribute+Sequence.swift"; path = "SwiftyAttributes/Sources/common/Attribute+Sequence.swift"; sourceTree = ""; }; + EC4BD01E9E340526FDB022B0AC660DB5 /* NSMutableAttributedString+SwiftyAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSMutableAttributedString+SwiftyAttributes.swift"; path = "SwiftyAttributes/Sources/common/NSMutableAttributedString+SwiftyAttributes.swift"; sourceTree = ""; }; + ED13170142C716E4F92927DB35C51BF6 /* SwiftyAttributes.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyAttributes.xcconfig; sourceTree = ""; }; + EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + F4708218757A4B6901FC841B2B59F829 /* SwiftyAttributes.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = SwiftyAttributes.modulemap; sourceTree = ""; }; + FA603788C28BF0BA02BD69CEFD0E933A /* AnimatedImageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedImageView.swift; path = Sources/AnimatedImageView.swift; sourceTree = ""; }; + FB1E4CB77C12BE4CFC21D6B7CF274364 /* Operators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = SwiftyAttributes/Sources/common/Operators.swift; sourceTree = ""; }; + FD19B69DAB3A1C127BD75A902646602B /* Kingfisher.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Kingfisher.xcconfig; sourceTree = ""; }; + FD2760F65D8A4A2AC461779728F1B440 /* Pods-App-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-App-frameworks.sh"; sourceTree = ""; }; + FDAFEF364B8446A2262A74E2C9F7B8FA /* MapError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MapError.swift; path = Sources/MapError.swift; sourceTree = ""; }; + FEDE850FB850BA4E97A2DA9A53D520C1 /* Toaster.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Toaster.h; path = Sources/Toaster.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 18612503C5A2846090E4B99DE7AF0182 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1875A69887F3DB414751C0FE1B008307 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 935D8A1CE694A5B3872F131DEF4618DF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8F4E41DB8356A898FD939835FFA9D9B5 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 96AFDC78814345BB829E8686F34F5353 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D0A478F606AE848C8EA23D439F0E628C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A674556B20717C2E0751C197AAEF8C66 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4DFCF4DAA9D6E29E454F60FD792808E3 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6978E37089917CDA587B9AA071EA425 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 250BACC429DD19E9D523CC55E4F55158 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BEC129A2A366A087EF0A93887359483E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A039D5F98F4C1621CDA454999C29F1C6 /* Foundation.framework in Frameworks */, + ACF61BFE7A8F95270DF2C0AA6D7A221D /* QuartzCore.framework in Frameworks */, + 78BE53AA86407BA8F49F4E3CB49870E7 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D94F5382CDAB5D880E64825FD4ACE2BF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1FCE40F1294F6F419261CE2124FAA1B8 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ED03E5917B811BACFE75189AE0C2A469 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 92136C6584624998D5005F46C70A628B /* CFNetwork.framework in Frameworks */, + 9C99E45B90BD95712D9878994BFC8B88 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0C813865BB8828808F1E0352A55D00D7 /* iOS */ = { + isa = PBXGroup; + children = ( + CE8498F0254A5E78F9F55D8898559BF8 /* CFNetwork.framework */, + EF023BC6C8365817EC30DFB8682078B3 /* Foundation.framework */, + 79690461CB9B6AFD42324392012C8FC9 /* QuartzCore.framework */, + 83228F1C33EACB8B498EACE0A47D4E70 /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 10A1EC12B7DB4A4BE7B38B0860491555 /* ObjectMapper */ = { + isa = PBXGroup; + children = ( + 69F57B0F8F4C1B84A8CDC1C273BEEEB5 /* CustomDateFormatTransform.swift */, + AFADAB654BF8506E72366E190401BA55 /* DataTransform.swift */, + 1CE05A98CF2B573BDBF51E1FE55675D5 /* DateFormatterTransform.swift */, + 491BE29441BA0CC5EFFB8F831470DB1D /* DateTransform.swift */, + 2FD0A16D8FF099986844552811646D57 /* DictionaryTransform.swift */, + B58285F094FF4CA5273C1242638D6D98 /* EnumOperators.swift */, + CC29AC28A057D9B3E17B259B41BDAE01 /* EnumTransform.swift */, + 9AFF8B1591956A1E6621F95F7DA49D8A /* FromJSON.swift */, + B6719DBD92EE3C4A0C5E03745E62C675 /* HexColorTransform.swift */, + AC0DB1AE78988360B610A23EDEBAEB2D /* ImmutableMappable.swift */, + DA7DEFDB704E47359290737774678D0E /* ISO8601DateTransform.swift */, + 16970D610E9E01EF4B23E2E3FCEB23AB /* Map.swift */, + FDAFEF364B8446A2262A74E2C9F7B8FA /* MapError.swift */, + 280E0ED002649293BC65E5AB962A5C42 /* Mappable.swift */, + E7DA7769B172D024B0267D2A2069B69B /* Mapper.swift */, + D73F73B2A2B769D1D99A14E6E138D1B9 /* NSDecimalNumberTransform.swift */, + BF39590435847619C2F7AAAD9D44A4D2 /* Operators.swift */, + 6064D056B5B49B6DBF5E344216CAB4B1 /* ToJSON.swift */, + 900298F507328C7282016C33D1F41F6B /* TransformOf.swift */, + ABA8A51F7726B8101FE20CB1A54CFE00 /* TransformOperators.swift */, + 56B4F865BA04E00E88D0C5A5035411D7 /* TransformType.swift */, + 8341ED5432782584EEBFDC4D63AC2EB6 /* URLTransform.swift */, + A6F7833941AE86FC9AE161BDA92BEFCB /* Support Files */, + ); + name = ObjectMapper; + path = ObjectMapper; + sourceTree = ""; + }; + 14B8B9B15ECBE87983FF987239AB2D7B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C813865BB8828808F1E0352A55D00D7 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 18B9E30E40BDA81DE9B55B434D3E5418 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + CAACC5CC124A9CB4BCA9E985EE2865AC /* Pods-App */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 3D71C5FCBCBCCDE2C7FF003C53F50CBD /* Pods */ = { + isa = PBXGroup; + children = ( + 6ABC2487B2D5B14D0099F73E1F63A2EC /* Kingfisher */, + 10A1EC12B7DB4A4BE7B38B0860491555 /* ObjectMapper */, + 5E3128E4B87BC28428C55A2AD430723E /* SCLAlertView */, + F7B73CDAF989EAD149E59497EA2F7EE4 /* SwiftOverlays */, + F0B213C9BD39DA17D20D27DEC2CDE4DC /* SwiftyAttributes */, + BB9C0677D0B40E1865DF7C16F95EA107 /* SwiftyJSON */, + BB61B7196F84F903DC002CA66CBD2936 /* Toaster */, + ); + name = Pods; + sourceTree = ""; + }; + 4322160836A6EC93AD395EB7D5C71A0F /* Products */ = { + isa = PBXGroup; + children = ( + 055AA51A37EAA0C48CF46D2396778291 /* Kingfisher.framework */, + 1CBFE1F0D19F610E66A895D50B4D6E40 /* ObjectMapper.framework */, + 026462E74CBE6FB9F060C0ACEDDCFE46 /* Pods_App.framework */, + B5D2FAFC8B327CB70015BE9FED6214E0 /* SCLAlertView.framework */, + 7EBD7B01DB6A7FA2190280D7CE39C5D4 /* SwiftOverlays.framework */, + 1C26B04D8FB07ABC60CD51CF27FF0CF9 /* SwiftyAttributes.framework */, + B6B418F8AEB6C257B47844EA27C423FC /* SwiftyJSON.framework */, + 9F136E99B9546D10971B0BBF42A37AB8 /* Toaster.framework */, + ); + name = Products; + sourceTree = ""; + }; + 51C3DD3BF0F64903A469D27C0A081D12 /* Support Files */ = { + isa = PBXGroup; + children = ( + 309BDAF707A8AD2B50EB2AF12220AB0E /* Info.plist */, + 50E5A1196EB41EFC077A3B6C8A17281D /* Kingfisher.modulemap */, + FD19B69DAB3A1C127BD75A902646602B /* Kingfisher.xcconfig */, + 0D88417549C207E0C2BBCA6F4CEFFF6C /* Kingfisher-dummy.m */, + 704449965FC05B7C43A7FEE564D45E4A /* Kingfisher-prefix.pch */, + 025D1FF262144C849C82C96AB0F0853C /* Kingfisher-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Kingfisher"; + sourceTree = ""; + }; + 5E3128E4B87BC28428C55A2AD430723E /* SCLAlertView */ = { + isa = PBXGroup; + children = ( + 390D503FED120763343D04B8903A3637 /* SCLAlertView.swift */, + 87F4728D4A15B3D0E9788EF2EB5E6B68 /* SCLExtensions.swift */, + FF7CE6464454DC685E076637CA12C842 /* Support Files */, + ); + name = SCLAlertView; + path = SCLAlertView; + sourceTree = ""; + }; + 6ABC2487B2D5B14D0099F73E1F63A2EC /* Kingfisher */ = { + isa = PBXGroup; + children = ( + FA603788C28BF0BA02BD69CEFD0E933A /* AnimatedImageView.swift */, + C79EC1DDFAD34B5791B4613CEEF8CF5C /* Box.swift */, + 340EEE1949B0F8C2CA4E8B7CEFE298BE /* CacheSerializer.swift */, + B7518259D580A169721D720B2E2C30C0 /* Filter.swift */, + E759F3003107DA8BACAC0224684852C9 /* Image.swift */, + 09D40E9C4B0A394CAF0C5D6D9D98A7DF /* ImageCache.swift */, + 0D380CFFDCCAACBCFC39601CD014F25F /* ImageDownloader.swift */, + C4B0D191B94EB8AA1D1A8AA0DD5EDE9C /* ImagePrefetcher.swift */, + 2ECA4696212612940D834EC974AD6C52 /* ImageProcessor.swift */, + 707FFCAB7EBA4D017FB00BD8A9DF4C01 /* ImageTransition.swift */, + BBC3F63626429F7135744B2C33A89E2E /* ImageView+Kingfisher.swift */, + 34BFB5C2202954F2262AB92C14993B8A /* Indicator.swift */, + 7DC769A17E31DA1E34AB7281B7E0D844 /* Kingfisher.h */, + 180C502F645A5C61CD6623EF9E0DA8AA /* Kingfisher.swift */, + EA5910BE73A82353E1F6B92E7F80D93C /* KingfisherManager.swift */, + A684757E050ABE8CA5F8BAA3D278DBF3 /* KingfisherOptionsInfo.swift */, + AA237330DEC4CC6B1FE401275CC67ADA /* RequestModifier.swift */, + D3926BAABA75706BCFA2B11502C90BCA /* Resource.swift */, + BA710CB29F55225463221333CCA78D5A /* String+MD5.swift */, + A37E90DC35456D87ECD8576ED2CA27FD /* ThreadHelper.swift */, + 1E4195BEEFCCEF86C9EFD8459C31F4BD /* UIButton+Kingfisher.swift */, + 51C3DD3BF0F64903A469D27C0A081D12 /* Support Files */, + ); + name = Kingfisher; + path = Kingfisher; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 14B8B9B15ECBE87983FF987239AB2D7B /* Frameworks */, + 3D71C5FCBCBCCDE2C7FF003C53F50CBD /* Pods */, + 4322160836A6EC93AD395EB7D5C71A0F /* Products */, + 18B9E30E40BDA81DE9B55B434D3E5418 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 83E0FBCCA30898CE4B84D3B33FFA71BA /* Support Files */ = { + isa = PBXGroup; + children = ( + 4FC1BCA0CEB5F1BF08672ACFD5DF727E /* Info.plist */, + 9A61AF76E37367E03E50E27A812699A9 /* Toaster.modulemap */, + BB4F1350996DEA205A6A88B280642397 /* Toaster.xcconfig */, + 5CC933E7D65DBFA580335BDAA6065A3B /* Toaster-dummy.m */, + 1D6EA002525E00EB47878C00F75C6E12 /* Toaster-prefix.pch */, + 2CF6B7BA42D0E502485F3CEB980E7F4C /* Toaster-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Toaster"; + sourceTree = ""; + }; + A6F7833941AE86FC9AE161BDA92BEFCB /* Support Files */ = { + isa = PBXGroup; + children = ( + 994D343D14F4A11A24B21065EBDD92E4 /* Info.plist */, + 5645958D70DCD5127C0053726750FD6A /* ObjectMapper.modulemap */, + 188406E777C0E9BDB827BC8C6A38363B /* ObjectMapper.xcconfig */, + 53FA93B929987B8CFC517E51EE330FDC /* ObjectMapper-dummy.m */, + 9EF6900B32D2A06A0EBEE4DB2950505B /* ObjectMapper-prefix.pch */, + 6AE7229A5BBAD52A1C548A418E532AAD /* ObjectMapper-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/ObjectMapper"; + sourceTree = ""; + }; + ADA6939AA5E71C9A5741EAC4F1DBD18B /* Support Files */ = { + isa = PBXGroup; + children = ( + 42AEFD34151B6F02C5D1E69B3CD61433 /* Info.plist */, + AB2270341E9944D2771F0BC0377F8000 /* SwiftyJSON.modulemap */, + 168DC307D781ED1E4AF0F390499BB905 /* SwiftyJSON.xcconfig */, + BECB5CD445868E338328202F763379D9 /* SwiftyJSON-dummy.m */, + 60D98A67B51133212D464D292DB8AD90 /* SwiftyJSON-prefix.pch */, + 5701D976FE49EEBC5F934D8CBD3ADC4F /* SwiftyJSON-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftyJSON"; + sourceTree = ""; + }; + BB61B7196F84F903DC002CA66CBD2936 /* Toaster */ = { + isa = PBXGroup; + children = ( + 9BCB19DEF359F965F3DF4D2ED08DB7CE /* Toast.swift */, + 0C3B453C1649585ADC1F7756ACBAAB3D /* ToastCenter.swift */, + FEDE850FB850BA4E97A2DA9A53D520C1 /* Toaster.h */, + E57B85CC6CE02A0A151F6AB7AAB3E871 /* ToastView.swift */, + A91412C7E96D6197059C9691DBFB5223 /* ToastWindow.swift */, + 83E0FBCCA30898CE4B84D3B33FFA71BA /* Support Files */, + ); + name = Toaster; + path = Toaster; + sourceTree = ""; + }; + BB9C0677D0B40E1865DF7C16F95EA107 /* SwiftyJSON */ = { + isa = PBXGroup; + children = ( + C6994D1565179C1B03368B8B9E69F0FD /* SwiftyJSON.swift */, + ADA6939AA5E71C9A5741EAC4F1DBD18B /* Support Files */, + ); + name = SwiftyJSON; + path = SwiftyJSON; + sourceTree = ""; + }; + CAACC5CC124A9CB4BCA9E985EE2865AC /* Pods-App */ = { + isa = PBXGroup; + children = ( + CCD8EEF11AF208CB282FB69552083F94 /* Info.plist */, + 4FAC16DF70DE22A85BBF55E089BB56E7 /* Pods-App.modulemap */, + 337EAD924FD67F5B4E29E4188F340512 /* Pods-App-acknowledgements.markdown */, + 3D0504B01F2638FBA19B4FC160EE5B1B /* Pods-App-acknowledgements.plist */, + 5C8144347BC58521E07DC398E6B01780 /* Pods-App-dummy.m */, + FD2760F65D8A4A2AC461779728F1B440 /* Pods-App-frameworks.sh */, + 94038376D370146BBE1F9C238F6BAF36 /* Pods-App-resources.sh */, + CFB9B6F0F36ECB44627648DFF07978C2 /* Pods-App-umbrella.h */, + 478D2A40B9F6CD2FA965CCD860EAFB49 /* Pods-App.debug.xcconfig */, + 6334E785167733FEBB6F54A43C3854EB /* Pods-App.release.xcconfig */, + ); + name = "Pods-App"; + path = "Target Support Files/Pods-App"; + sourceTree = ""; + }; + E678114DE549F8F55D11FD6B11F4C5F1 /* Support Files */ = { + isa = PBXGroup; + children = ( + ACC2678249D07007C9BF118E3EB3AA90 /* Info.plist */, + 3420D5FAFC83638FB809D52289603DF1 /* SwiftOverlays.modulemap */, + 6549790AD76DA84F6E0B5B5C4194A42A /* SwiftOverlays.xcconfig */, + DAF1FCBEA2CFD6AF0F728F87094A1C58 /* SwiftOverlays-dummy.m */, + 41170103A297A0F759E307D01F51384A /* SwiftOverlays-prefix.pch */, + 61ECE3A023C33165A4A356F6D053013C /* SwiftOverlays-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftOverlays"; + sourceTree = ""; + }; + F0B213C9BD39DA17D20D27DEC2CDE4DC /* SwiftyAttributes */ = { + isa = PBXGroup; + children = ( + B4EF2B97F288CBD06982F5670B01D356 /* Attribute.swift */, + EA928CA7DFCF1CB55D26CDC1C144B58E /* Attribute+Sequence.swift */, + 1886F3B95F12A912EF8F352468DA13C9 /* AttributeName.swift */, + CB4138194D6E12FA8809A2D09AAD682C /* Ligatures.swift */, + 8F081FAD8410108B8DC76764B1D3F07E /* NSAttributedString+SwiftyAttributes.swift */, + EC4BD01E9E340526FDB022B0AC660DB5 /* NSMutableAttributedString+SwiftyAttributes.swift */, + FB1E4CB77C12BE4CFC21D6B7CF274364 /* Operators.swift */, + BE1BC7F28F04A50779F991E0DA6EFF4E /* String+SwiftyAttributes.swift */, + CB0A17E8698564E0CA4C5177D1D6A15D /* TextEffect.swift */, + 20F1502CBBBC8C06983666D5A8BADB80 /* VerticalGlyphForm.swift */, + D1683433091A07D620395F0C34A5E76C /* WritingDirection.swift */, + FFD37081AB87030F9CF52720F14D0D47 /* Support Files */, + ); + name = SwiftyAttributes; + path = SwiftyAttributes; + sourceTree = ""; + }; + F7B73CDAF989EAD149E59497EA2F7EE4 /* SwiftOverlays */ = { + isa = PBXGroup; + children = ( + AAF4DF6A946EA6940303272FA0422F80 /* SwiftOverlays.swift */, + E678114DE549F8F55D11FD6B11F4C5F1 /* Support Files */, + ); + name = SwiftOverlays; + path = SwiftOverlays; + sourceTree = ""; + }; + FF7CE6464454DC685E076637CA12C842 /* Support Files */ = { + isa = PBXGroup; + children = ( + 69F22F903747E5C0A1EF6C98A017744E /* Info.plist */, + 8B4B7698B8EC9934045037500FC9A07F /* SCLAlertView.modulemap */, + 6EECB0A3DE4E4CFD8F4D489851D461C0 /* SCLAlertView.xcconfig */, + D9279CA4C2057BA57D5A23229CB1CB73 /* SCLAlertView-dummy.m */, + DBC74A2214EDE552725FB5B35CDB5D3B /* SCLAlertView-prefix.pch */, + A531FF0DFC411748F044F7DB009026C2 /* SCLAlertView-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SCLAlertView"; + sourceTree = ""; + }; + FFD37081AB87030F9CF52720F14D0D47 /* Support Files */ = { + isa = PBXGroup; + children = ( + A9DECBB12CB7C070F203B7FD27B1A9CF /* Info.plist */, + F4708218757A4B6901FC841B2B59F829 /* SwiftyAttributes.modulemap */, + ED13170142C716E4F92927DB35C51BF6 /* SwiftyAttributes.xcconfig */, + 5575B1EFE5F487F89620EE6EDDA8B3F2 /* SwiftyAttributes-dummy.m */, + 49DFA38C51AC6D425719179BB3442E57 /* SwiftyAttributes-prefix.pch */, + B321DA619965869FF11C12A069510605 /* SwiftyAttributes-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftyAttributes"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0F94B6BFE5A6242FBBB0BE3D27F7CEE9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A8F35D1EBAEF99D38F02A0B8CA5D8FBE /* Kingfisher-umbrella.h in Headers */, + 179523942E7D1E3EB210EF05E2C7AE79 /* Kingfisher.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1F5BD41C6285690C737C15ECDCCE1D44 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + BA1EE53EB078061EF3DE750109DBB170 /* SCLAlertView-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8C335EDE0B5FAC1B23B05772FC8DB63D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5BDD00BC7828B3B2D99C575577C82DCC /* SwiftyAttributes-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B04E199B379FC7EEA126F8093F201B1C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A621C1E0E3AE11E996301062239C4018 /* SwiftOverlays-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2AAC3CCC4B5A22BDAE6BCF9D33F80E0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0358D17087455057141DE3FB27665852 /* Pods-App-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D96C856F63F9467C803C72419C02117E /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D3178EA66917B1D492F59DA298436ED7 /* Toaster-umbrella.h in Headers */, + D899EDF480F3D18FCF8AC924ECF71BF6 /* Toaster.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + ED0EE4E94048582C0950E1D3F036DA9A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1234D5A1AC0B7C352DB5562859D68EDC /* SwiftyJSON-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F3BFA283BF3122E163C4489C254D9862 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0B27203E9B160A390F5E17796DE86932 /* ObjectMapper-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 17F8283B6318A06B3FE0121AB69E337E /* SwiftOverlays */ = { + isa = PBXNativeTarget; + buildConfigurationList = D0C8551FABF3C906A3F4B73FD7038B98 /* Build configuration list for PBXNativeTarget "SwiftOverlays" */; + buildPhases = ( + 3D316DF985BA48AB685299B42442DEF6 /* Sources */, + 935D8A1CE694A5B3872F131DEF4618DF /* Frameworks */, + B04E199B379FC7EEA126F8093F201B1C /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftOverlays; + productName = SwiftOverlays; + productReference = 7EBD7B01DB6A7FA2190280D7CE39C5D4 /* SwiftOverlays.framework */; + productType = "com.apple.product-type.framework"; + }; + 27801029A78BC07E067F119CE3349CA6 /* SwiftyJSON */ = { + isa = PBXNativeTarget; + buildConfigurationList = 66C471792F62BD9343E7FFB0F92BE188 /* Build configuration list for PBXNativeTarget "SwiftyJSON" */; + buildPhases = ( + 4DC6CA51CFDD683AF00E3E4FC6AF615D /* Sources */, + A674556B20717C2E0751C197AAEF8C66 /* Frameworks */, + ED0EE4E94048582C0950E1D3F036DA9A /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftyJSON; + productName = SwiftyJSON; + productReference = B6B418F8AEB6C257B47844EA27C423FC /* SwiftyJSON.framework */; + productType = "com.apple.product-type.framework"; + }; + 3B141857F2FFD80B944B2DD19BA482A8 /* Pods-App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3D2940BE9DC9DFF0E3B309E1A32FD8EB /* Build configuration list for PBXNativeTarget "Pods-App" */; + buildPhases = ( + B53A3A258BF732AAF8415CAA315A5A2A /* Sources */, + 96AFDC78814345BB829E8686F34F5353 /* Frameworks */, + C2AAC3CCC4B5A22BDAE6BCF9D33F80E0 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 86E71E6EA40420BCE0373E34E6F15DF6 /* PBXTargetDependency */, + 31D0182F691FAC0A13B6566DFE5314A2 /* PBXTargetDependency */, + 1DB72825AD876F571CE156BF44ACDD44 /* PBXTargetDependency */, + D9E54AE899043C5686632ACDE95668EA /* PBXTargetDependency */, + DD7B4D81F8223C943A644C5D445E8D95 /* PBXTargetDependency */, + F6949FB29299B2D88B34A4959E70BF1F /* PBXTargetDependency */, + 1BBF456645674CE81FF95C5DE0C9923E /* PBXTargetDependency */, + ); + name = "Pods-App"; + productName = "Pods-App"; + productReference = 026462E74CBE6FB9F060C0ACEDDCFE46 /* Pods_App.framework */; + productType = "com.apple.product-type.framework"; + }; + ADCF391CDDFA64415DE4E0FEE7223817 /* Toaster */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2BA882386909EDEAD28321CD0B625BFE /* Build configuration list for PBXNativeTarget "Toaster" */; + buildPhases = ( + D17D8FAB6F12AE7F1C234178548E00D2 /* Sources */, + BEC129A2A366A087EF0A93887359483E /* Frameworks */, + D96C856F63F9467C803C72419C02117E /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Toaster; + productName = Toaster; + productReference = 9F136E99B9546D10971B0BBF42A37AB8 /* Toaster.framework */; + productType = "com.apple.product-type.framework"; + }; + B43918AB915242B851ABEFB5D70BE0AD /* ObjectMapper */ = { + isa = PBXNativeTarget; + buildConfigurationList = F89BCF8F71331FFD619BCBAFFC87F2A4 /* Build configuration list for PBXNativeTarget "ObjectMapper" */; + buildPhases = ( + BD107137834CD6974D07FBBE5DACD957 /* Sources */, + B6978E37089917CDA587B9AA071EA425 /* Frameworks */, + F3BFA283BF3122E163C4489C254D9862 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ObjectMapper; + productName = ObjectMapper; + productReference = 1CBFE1F0D19F610E66A895D50B4D6E40 /* ObjectMapper.framework */; + productType = "com.apple.product-type.framework"; + }; + C4944FEC314D1A66588651D006273ADE /* Kingfisher */ = { + isa = PBXNativeTarget; + buildConfigurationList = 76E7EFBCEA1340954A03AA68051C2306 /* Build configuration list for PBXNativeTarget "Kingfisher" */; + buildPhases = ( + EAC7EA95310C5E1E724C632852F7D186 /* Sources */, + ED03E5917B811BACFE75189AE0C2A469 /* Frameworks */, + 0F94B6BFE5A6242FBBB0BE3D27F7CEE9 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Kingfisher; + productName = Kingfisher; + productReference = 055AA51A37EAA0C48CF46D2396778291 /* Kingfisher.framework */; + productType = "com.apple.product-type.framework"; + }; + CDBB4A964696BB8EDEFFCAE8F72ED4F0 /* SCLAlertView */ = { + isa = PBXNativeTarget; + buildConfigurationList = B73451596133D494DD61F61186284534 /* Build configuration list for PBXNativeTarget "SCLAlertView" */; + buildPhases = ( + 6D8B7DC5CF6C72820D5A6C82F77E4584 /* Sources */, + D94F5382CDAB5D880E64825FD4ACE2BF /* Frameworks */, + 1F5BD41C6285690C737C15ECDCCE1D44 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SCLAlertView; + productName = SCLAlertView; + productReference = B5D2FAFC8B327CB70015BE9FED6214E0 /* SCLAlertView.framework */; + productType = "com.apple.product-type.framework"; + }; + E951149A9EEDC6743F5EDBC1B869A3A3 /* SwiftyAttributes */ = { + isa = PBXNativeTarget; + buildConfigurationList = B08567B69FE71BACC0AB40131E042DB3 /* Build configuration list for PBXNativeTarget "SwiftyAttributes" */; + buildPhases = ( + 0918AAE80968F6F98F82551D1D197C52 /* Sources */, + 18612503C5A2846090E4B99DE7AF0182 /* Frameworks */, + 8C335EDE0B5FAC1B23B05772FC8DB63D /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftyAttributes; + productName = SwiftyAttributes; + productReference = 1C26B04D8FB07ABC60CD51CF27FF0CF9 /* SwiftyAttributes.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = 4322160836A6EC93AD395EB7D5C71A0F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C4944FEC314D1A66588651D006273ADE /* Kingfisher */, + B43918AB915242B851ABEFB5D70BE0AD /* ObjectMapper */, + 3B141857F2FFD80B944B2DD19BA482A8 /* Pods-App */, + CDBB4A964696BB8EDEFFCAE8F72ED4F0 /* SCLAlertView */, + 17F8283B6318A06B3FE0121AB69E337E /* SwiftOverlays */, + E951149A9EEDC6743F5EDBC1B869A3A3 /* SwiftyAttributes */, + 27801029A78BC07E067F119CE3349CA6 /* SwiftyJSON */, + ADCF391CDDFA64415DE4E0FEE7223817 /* Toaster */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0918AAE80968F6F98F82551D1D197C52 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4BDEF50B66EF9FE4AB936903B5605FC8 /* Attribute+Sequence.swift in Sources */, + 7912101A1E1270B55E2D09DE41BAC29A /* Attribute.swift in Sources */, + CBA1828775284D4B79D101BFC06B890E /* AttributeName.swift in Sources */, + 8CEC01EF09ADC0B68F2F8FB26E49A3EC /* Ligatures.swift in Sources */, + 7D54B5190E71C28EFCACCD4C79F1D112 /* NSAttributedString+SwiftyAttributes.swift in Sources */, + 4FC991960B752A636DAB258F0E7A5D08 /* NSMutableAttributedString+SwiftyAttributes.swift in Sources */, + 50F243DC169000D26B57BF1EDB636EEA /* Operators.swift in Sources */, + 3EA3F0AC878080E9081A5ACD45EBC93C /* String+SwiftyAttributes.swift in Sources */, + 332E04A8CC0927C79942013D9E7EEDF9 /* SwiftyAttributes-dummy.m in Sources */, + 4C9B4B9B7D3C32DF1C06A37502D4950D /* TextEffect.swift in Sources */, + A7E5D232DC1007984ED91E0CFC80E496 /* VerticalGlyphForm.swift in Sources */, + 7DB2736C0961E9ACE1ECC680C121B5C7 /* WritingDirection.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3D316DF985BA48AB685299B42442DEF6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C13A63D03E1033AF9BF05013DE75CF65 /* SwiftOverlays-dummy.m in Sources */, + B4C5619F183A5A456B541B0E737AA13B /* SwiftOverlays.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4DC6CA51CFDD683AF00E3E4FC6AF615D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6E29549E7FE6860985B2FF6FC82F0451 /* SwiftyJSON-dummy.m in Sources */, + 24B00DE95ABD4CCEB5319E522259AC88 /* SwiftyJSON.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6D8B7DC5CF6C72820D5A6C82F77E4584 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61EDCBC66BA7717EE6FCDBDF4563082C /* SCLAlertView-dummy.m in Sources */, + 1C782F5035E677BF10E972411015B275 /* SCLAlertView.swift in Sources */, + 3E481544A395D7DBD4677A764E36E49C /* SCLExtensions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B53A3A258BF732AAF8415CAA315A5A2A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D4679FA67F8376FB88C226BA44A0E939 /* Pods-App-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BD107137834CD6974D07FBBE5DACD957 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7183AD43047C455B682A4020359C2A5E /* CustomDateFormatTransform.swift in Sources */, + E9D0A1BD282C3D6E46D284644A676921 /* DataTransform.swift in Sources */, + 314B9E9F909985AD40B416A0CE4D8F51 /* DateFormatterTransform.swift in Sources */, + 4FDEABA45359B146DF34457AFD40CB35 /* DateTransform.swift in Sources */, + A5014B98E0B339947198C6803DE634E8 /* DictionaryTransform.swift in Sources */, + 2868717245A49E3E130B53CC1BE7C123 /* EnumOperators.swift in Sources */, + 45CA1F8F65C0E30B5402A998E15DEBB6 /* EnumTransform.swift in Sources */, + DC1F12C360F2419DB390C24A7AC772D4 /* FromJSON.swift in Sources */, + A83C9438D9D5695F8E18FB652CA9F168 /* HexColorTransform.swift in Sources */, + 4421476F7F26886F96E830C664C39FA3 /* ImmutableMappable.swift in Sources */, + 12EF25578B49A115595FDFCCF1B4EDD0 /* ISO8601DateTransform.swift in Sources */, + 4C86D4E914CE708EDEC9B00D9E32770C /* Map.swift in Sources */, + 4C8E3610E9DEE3F3EF326AEE6C104DB5 /* MapError.swift in Sources */, + 3168742F7D72DF80FA485C69BA3B9D90 /* Mappable.swift in Sources */, + EE3A19C18674237A97F15FE09BE71EA3 /* Mapper.swift in Sources */, + 9663A29E978A80886C5FA9865AEB220C /* NSDecimalNumberTransform.swift in Sources */, + B3F322D2C4B88A3EE24DA8797D1F3189 /* ObjectMapper-dummy.m in Sources */, + FEEF32367907C0D129AD9DE56F6932D5 /* Operators.swift in Sources */, + 7F1E9E1E1B49552F24FB32C65B9FEB7A /* ToJSON.swift in Sources */, + 42F182CCC2C8B7521C2ABBD5D61B8F67 /* TransformOf.swift in Sources */, + F6EC7E4B2FA0CEF7F9A36AC4444B71DB /* TransformOperators.swift in Sources */, + A6C2F066D75588425CC85D20985C2EE2 /* TransformType.swift in Sources */, + 191223246695B518ECCF7206E518725B /* URLTransform.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D17D8FAB6F12AE7F1C234178548E00D2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E776A6BEB74DF1AEB7460F71894BF253 /* Toast.swift in Sources */, + F3AED763881AAEE63A7A927498C32ACC /* ToastCenter.swift in Sources */, + D6AC19ADE405294C55EA86A5251D92C2 /* Toaster-dummy.m in Sources */, + 6F8FA966CB8A3AEF6F93ED7FFF58DB4F /* ToastView.swift in Sources */, + 022BA98FB777CB61FCEEC1E18426A054 /* ToastWindow.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EAC7EA95310C5E1E724C632852F7D186 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 342AE33215135AD56B73580012DC22AB /* AnimatedImageView.swift in Sources */, + 6DA770087D354CDCD3889CDAE7C4C698 /* Box.swift in Sources */, + 2E5E570FFC49EF98550401421A243C0F /* CacheSerializer.swift in Sources */, + 4089E39F76F86466B8DDD39A1C1ABB6B /* Filter.swift in Sources */, + 33A14E817C0FA585D5DA3A55E453299D /* Image.swift in Sources */, + F8E957950566622A3E4B61AACE8F0B4A /* ImageCache.swift in Sources */, + DC6080C64C269628C3F5107BE87055E2 /* ImageDownloader.swift in Sources */, + 3414E1D9CB15149A296E16C45967B021 /* ImagePrefetcher.swift in Sources */, + 0E9CC28AC8E34FE8E1C87E933E04BC7A /* ImageProcessor.swift in Sources */, + 86B7DA678C8705523C300947117C0706 /* ImageTransition.swift in Sources */, + 33395912F2FFA355C4C36B7EDEAA3DA7 /* ImageView+Kingfisher.swift in Sources */, + 52A280AB4852FDE92DF09B5E79D5C2AA /* Indicator.swift in Sources */, + 6A375593407BC7937234B2B6A0811760 /* Kingfisher-dummy.m in Sources */, + C9CC5ED97D29C6C3DE22B78A9AA2AD91 /* Kingfisher.swift in Sources */, + 7636F846675C9F4ED96E00C6C19A4890 /* KingfisherManager.swift in Sources */, + 378B44CAA91C2106076668FDE72E1E02 /* KingfisherOptionsInfo.swift in Sources */, + EA816A580C3858934ED6FE92040C26DD /* RequestModifier.swift in Sources */, + 8DA7E6883118CB3C8132B64D3D43AE0B /* Resource.swift in Sources */, + 9F826F893E86EB86591B637A1CE4B13A /* String+MD5.swift in Sources */, + 6CD63E1564618B6213B98E0ABBC3127C /* ThreadHelper.swift in Sources */, + EE89512E8D36E67CDC525DB9D5BBF5A7 /* UIButton+Kingfisher.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1BBF456645674CE81FF95C5DE0C9923E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Toaster; + target = ADCF391CDDFA64415DE4E0FEE7223817 /* Toaster */; + targetProxy = A2BD1E3D7B87DE6507BA0DF6BE473DB9 /* PBXContainerItemProxy */; + }; + 1DB72825AD876F571CE156BF44ACDD44 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SCLAlertView; + target = CDBB4A964696BB8EDEFFCAE8F72ED4F0 /* SCLAlertView */; + targetProxy = D2D8D7183AA7BC95B977D32693E4F2F9 /* PBXContainerItemProxy */; + }; + 31D0182F691FAC0A13B6566DFE5314A2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = ObjectMapper; + target = B43918AB915242B851ABEFB5D70BE0AD /* ObjectMapper */; + targetProxy = 274FFB0326D4DE33CF6CBDABA7FCEDFE /* PBXContainerItemProxy */; + }; + 86E71E6EA40420BCE0373E34E6F15DF6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Kingfisher; + target = C4944FEC314D1A66588651D006273ADE /* Kingfisher */; + targetProxy = 49E86076679F7D3B63E967DA7DC2F3D6 /* PBXContainerItemProxy */; + }; + D9E54AE899043C5686632ACDE95668EA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftOverlays; + target = 17F8283B6318A06B3FE0121AB69E337E /* SwiftOverlays */; + targetProxy = 6A7EC7087197BFC36F32E6F684FBC679 /* PBXContainerItemProxy */; + }; + DD7B4D81F8223C943A644C5D445E8D95 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyAttributes; + target = E951149A9EEDC6743F5EDBC1B869A3A3 /* SwiftyAttributes */; + targetProxy = 520A4997E95787B084FC97ADD8D0A7DC /* PBXContainerItemProxy */; + }; + F6949FB29299B2D88B34A4959E70BF1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyJSON; + target = 27801029A78BC07E067F119CE3349CA6 /* SwiftyJSON */; + targetProxy = EB4188BC3B1B40F0A7B4D25C6A1F9244 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 10C89E4D490C226212049B7B3545750D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BB4F1350996DEA205A6A88B280642397 /* Toaster.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Toaster/Toaster-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Toaster/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Toaster/Toaster.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = Toaster; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 1613E780F4024D1785A25A21C098B353 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6334E785167733FEBB6F54A43C3854EB /* Pods-App.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-App/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-App/Pods-App.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_App; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 229B03F37715506E586FBDEE32F6697C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EECB0A3DE4E4CFD8F4D489851D461C0 /* SCLAlertView.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SCLAlertView/SCLAlertView-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SCLAlertView/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SCLAlertView/SCLAlertView.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = SCLAlertView; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 253FCD6808B04C35B0821EF491B1248D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6549790AD76DA84F6E0B5B5C4194A42A /* SwiftOverlays.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftOverlays/SwiftOverlays-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftOverlays/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftOverlays/SwiftOverlays.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = SwiftOverlays; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2BF325C290F0D8B65BD69F35F802D863 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = ED13170142C716E4F92927DB35C51BF6 /* SwiftyAttributes.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyAttributes/SwiftyAttributes-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyAttributes/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyAttributes/SwiftyAttributes.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = SwiftyAttributes; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 380C30050278E488BE23DB926933212E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FD19B69DAB3A1C127BD75A902646602B /* Kingfisher.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = Kingfisher; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 3C9A9BEBDCF7660A62E238B58A066DB4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 168DC307D781ED1E4AF0F390499BB905 /* SwiftyJSON.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = SwiftyJSON; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 3F145B11D2FF864ADF033CB40427D12C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 188406E777C0E9BDB827BC8C6A38363B /* ObjectMapper.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/ObjectMapper/ObjectMapper-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/ObjectMapper/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/ObjectMapper/ObjectMapper.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = ObjectMapper; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 41B46A86AB1A81DC62B9CC84069FFCE0 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = ED13170142C716E4F92927DB35C51BF6 /* SwiftyAttributes.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyAttributes/SwiftyAttributes-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyAttributes/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyAttributes/SwiftyAttributes.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = SwiftyAttributes; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 489662FB56AAD674D9A06AC28ABE01DC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6EECB0A3DE4E4CFD8F4D489851D461C0 /* SCLAlertView.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SCLAlertView/SCLAlertView-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SCLAlertView/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SCLAlertView/SCLAlertView.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = SCLAlertView; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5D9010A4FCE8DBAB2716702E5CEC249B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6549790AD76DA84F6E0B5B5C4194A42A /* SwiftOverlays.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftOverlays/SwiftOverlays-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftOverlays/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftOverlays/SwiftOverlays.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = SwiftOverlays; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 673254EEAF0B5BF4596080C749645884 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6E53245BBAAAB71AD4E0EE52CA99FD28 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FD19B69DAB3A1C127BD75A902646602B /* Kingfisher.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = Kingfisher; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 99FCB7E8B26EB185BF4C5C19F33748E7 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 188406E777C0E9BDB827BC8C6A38363B /* ObjectMapper.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/ObjectMapper/ObjectMapper-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/ObjectMapper/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/ObjectMapper/ObjectMapper.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = ObjectMapper; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A215E8174AF0304D11DF9EC6922AE2C3 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BB4F1350996DEA205A6A88B280642397 /* Toaster.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Toaster/Toaster-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Toaster/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Toaster/Toaster.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = Toaster; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + B8F5BDBC1545345C9177E9E1D5B83059 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 478D2A40B9F6CD2FA965CCD860EAFB49 /* Pods-App.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "Target Support Files/Pods-App/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-App/Pods-App.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_App; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E4F26E6EB105713A6A7E7E2E283AC2DF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + ONLY_ACTIVE_ARCH = YES; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + F6947F56D63E0C1CBCDB13FB9E48E1A8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 168DC307D781ED1E4AF0F390499BB905 /* SwiftyJSON.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = SwiftyJSON; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2BA882386909EDEAD28321CD0B625BFE /* Build configuration list for PBXNativeTarget "Toaster" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A215E8174AF0304D11DF9EC6922AE2C3 /* Debug */, + 10C89E4D490C226212049B7B3545750D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E4F26E6EB105713A6A7E7E2E283AC2DF /* Debug */, + 673254EEAF0B5BF4596080C749645884 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3D2940BE9DC9DFF0E3B309E1A32FD8EB /* Build configuration list for PBXNativeTarget "Pods-App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B8F5BDBC1545345C9177E9E1D5B83059 /* Debug */, + 1613E780F4024D1785A25A21C098B353 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 66C471792F62BD9343E7FFB0F92BE188 /* Build configuration list for PBXNativeTarget "SwiftyJSON" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3C9A9BEBDCF7660A62E238B58A066DB4 /* Debug */, + F6947F56D63E0C1CBCDB13FB9E48E1A8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 76E7EFBCEA1340954A03AA68051C2306 /* Build configuration list for PBXNativeTarget "Kingfisher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6E53245BBAAAB71AD4E0EE52CA99FD28 /* Debug */, + 380C30050278E488BE23DB926933212E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B08567B69FE71BACC0AB40131E042DB3 /* Build configuration list for PBXNativeTarget "SwiftyAttributes" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 41B46A86AB1A81DC62B9CC84069FFCE0 /* Debug */, + 2BF325C290F0D8B65BD69F35F802D863 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B73451596133D494DD61F61186284534 /* Build configuration list for PBXNativeTarget "SCLAlertView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 489662FB56AAD674D9A06AC28ABE01DC /* Debug */, + 229B03F37715506E586FBDEE32F6697C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D0C8551FABF3C906A3F4B73FD7038B98 /* Build configuration list for PBXNativeTarget "SwiftOverlays" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 253FCD6808B04C35B0821EF491B1248D /* Debug */, + 5D9010A4FCE8DBAB2716702E5CEC249B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F89BCF8F71331FFD619BCBAFFC87F2A4 /* Build configuration list for PBXNativeTarget "ObjectMapper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3F145B11D2FF864ADF033CB40427D12C /* Debug */, + 99FCB7E8B26EB185BF4C5C19F33748E7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Kingfisher.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Kingfisher.xcscheme new file mode 100644 index 0000000..76c33f4 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Kingfisher.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/ObjectMapper.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/ObjectMapper.xcscheme new file mode 100644 index 0000000..b542047 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/ObjectMapper.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Pods-App.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Pods-App.xcscheme new file mode 100644 index 0000000..1b9e3c5 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Pods-App.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SCLAlertView.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SCLAlertView.xcscheme new file mode 100644 index 0000000..868a24e --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SCLAlertView.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftOverlays.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftOverlays.xcscheme new file mode 100644 index 0000000..afa8d29 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftOverlays.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyAttributes.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyAttributes.xcscheme new file mode 100644 index 0000000..3f5147a --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyAttributes.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyJSON.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyJSON.xcscheme new file mode 100644 index 0000000..6fe7877 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/SwiftyJSON.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Toaster.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Toaster.xcscheme new file mode 100644 index 0000000..e7b9f67 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Toaster.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..d77628d --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,108 @@ + + + + + SchemeUserState + + Kingfisher.xcscheme + + isShown + + orderHint + 0 + + ObjectMapper.xcscheme + + isShown + + orderHint + 1 + + Pods-App.xcscheme + + isShown + + orderHint + 2 + + SCLAlertView.xcscheme + + isShown + + orderHint + 3 + + SwiftOverlays.xcscheme + + isShown + + orderHint + 4 + + SwiftyAttributes.xcscheme + + isShown + + orderHint + 5 + + SwiftyJSON.xcscheme + + isShown + + orderHint + 6 + + Toaster.xcscheme + + isShown + + orderHint + 7 + + + SuppressBuildableAutocreation + + 17F8283B6318A06B3FE0121AB69E337E + + primary + + + 27801029A78BC07E067F119CE3349CA6 + + primary + + + 3B141857F2FFD80B944B2DD19BA482A8 + + primary + + + ADCF391CDDFA64415DE4E0FEE7223817 + + primary + + + B43918AB915242B851ABEFB5D70BE0AD + + primary + + + C4944FEC314D1A66588651D006273ADE + + primary + + + CDBB4A964696BB8EDEFFCAE8F72ED4F0 + + primary + + + E951149A9EEDC6743F5EDBC1B869A3A3 + + primary + + + + + diff --git a/Pods/SCLAlertView/LICENCE b/Pods/SCLAlertView/LICENCE new file mode 100644 index 0000000..46b7dbd --- /dev/null +++ b/Pods/SCLAlertView/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2014 SCPopUpView by Viktor Radchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/SCLAlertView/README.md b/Pods/SCLAlertView/README.md new file mode 100644 index 0000000..41c311a --- /dev/null +++ b/Pods/SCLAlertView/README.md @@ -0,0 +1,271 @@ +SCLAlertView +=========== + +[![Version](https://img.shields.io/cocoapods/v/SCLAlertView.svg?style=flat)](http://cocoadocs.org/docsets/SCLAlertView/) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +Animated Alert View written in Swift, which can be used as a `UIAlertView` or `UIAlertController` replacement. Since `UIAlertView` is deprecated and `UIAlertController` only works on iOS 8.x or above, if you have a Swift project where you want to support iOS 7.x too, SCLAlertView is an ideal substitution. + +![BackgroundImage](https://raw.githubusercontent.com/vikmeup/SCPopUpView/master/successScreenshot.png)_ +![BackgroundImage](https://raw.githubusercontent.com/vikmeup/SCPopUpView/master/editScreenshot.png) + +Easy to use +---- + +### Get Started + +```swift +// Get started +SCLAlertView().showInfo("Important info", subTitle: "You are great") +``` + +### Updating the alert view + +```swift +let alertViewResponder: SCLAlertViewResponder = SCLAlertView().showSuccess("Hello World", subTitle: "This is a more descriptive text.") + +// Upon displaying, change/close view +alertViewResponder.setTitle("New Title") // Rename title +alertViewResponder.setSubTitle("New description") // Rename subtitle +alertViewResponder.close() // Close view +``` + +### Alternative alert types + +``` +SCLAlertView().showError("Hello Error", subTitle: "This is a more descriptive error text.") // Error +SCLAlertView().showNotice("Hello Notice", subTitle: "This is a more descriptive notice text.") // Notice +SCLAlertView().showWarning("Hello Warning", subTitle: "This is a more descriptive warning text.") // Warning +SCLAlertView().showInfo("Hello Info", subTitle: "This is a more descriptive info text.") // Info +SCLAlertView().showEdit("Hello Edit", subTitle: "This is a more descriptive info text.") // Edit +``` + +### Raw call to showTitle() + +```swift +SCLAlertView().showTitle( + "Congratulations", // Title of view + subTitle: "Operation successfully completed.", // String of view + duration: 2.0, // Duration to show before closing automatically, default: 0.0 + completeText: "Done", // Optional button value, default: "" + style: .Success, // Styles - see below. + colorStyle: 0xA429FF, + colorTextButton: 0xFFFFFF +) +``` + +### Controls + +#### Custom Appearance + +```swift +// SCLAlertView.SCLAppearanc has more than 15 different properties to customize. See below. + +let appearance = SCLAlertView.SCLAppearance( + kTitleFont: UIFont(name: "HelveticaNeue", size: 20)!, + kTextFont: UIFont(name: "HelveticaNeue", size: 14)!, + kButtonFont: UIFont(name: "HelveticaNeue-Bold", size: 14)!, + showCloseButton: false +) + +let alert = SCLAlertView(appearance: appearance) +``` + +#### Add buttons + +```swift +let alertView = SCLAlertView() +alertView.addButton("First Button", target:self, selector:Selector("firstButton")) +alertView.addButton("Second Button") { + println("Second button tapped") +} +alertView.showSuccess("Button View", subTitle: "This alert view has buttons") +``` + +#### Hide default close button + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCloseButton: false +) +let alertView = SCLAlertView(appearance: appearance) +alertView.showSuccess("No button", subTitle: "You will have hard times trying to close me") +``` + +#### Hide default close button & a duration to close the alert + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCloseButton: false +) +let alertView = SCLAlertView(appearance: appearance) +alertView.showWarning("No button", subTitle: "Just wait for 3 seconds and I will disappear", duration: 3) +``` + + +#### Hide alert icon + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCircularIcon: false +) +let alertView = SCLAlertView(appearance: appearance) +alertView.showSuccess("No icon", subTitle: "This is a clean alert without Icon!") +``` + +#### Use a custom icon + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCircularIcon: true +) +let alertView = SCLAlertView(appearance: appearance) +let alertViewIcon = UIImage(named: "IconImage") //Replace the IconImage text with the image name +alertView.showInfo("Custom icon", subTitle: "This is a nice alert with a custom icon you choose", circleIconImage: alertViewIcon) +``` + + +#### Add Text fields + +```swift +// Add a text field +let alert = SCLAlertView() +let txt = alert.addTextField(title:"Enter your name") +alert.addButton("Show Name") { + println("Text value: \(txt.text)") +} +alert.showEdit("Edit View", subTitle: "This alert view shows a text box") +``` + +#### Use a custom subview instead of a subtitle +```swift +// Example of using the view to add two text fields to the alert +// Create the subview +let appearance = SCLAlertView.SCLAppearance( + kTitleFont: UIFont(name: "HelveticaNeue", size: 20)!, + kTextFont: UIFont(name: "HelveticaNeue", size: 14)!, + kButtonFont: UIFont(name: "HelveticaNeue-Bold", size: 14)!, + showCloseButton: false +) + +// Initialize SCLAlertView using custom Appearance +let alert = SCLAlertView(appearance: appearance) + +// Creat the subview +let subview = UIView(frame: CGRectMake(0,0,216,70)) +let x = (subview.frame.width - 180) / 2 + +// Add textfield 1 +let textfield1 = UITextField(frame: CGRectMake(x,10,180,25)) +textfield1.layer.borderColor = UIColor.greenColor().CGColor +textfield1.layer.borderWidth = 1.5 +textfield1.layer.cornerRadius = 5 +textfield1.placeholder = "Username" +textfield1.textAlignment = NSTextAlignment.Center +subview.addSubview(textfield1) + +// Add textfield 2 +let textfield2 = UITextField(frame: CGRectMake(x,textfield1.frame.maxY + 10,180,25)) +textfield2.secureTextEntry = true +textfield2.layer.borderColor = UIColor.blueColor().CGColor +textfield2.layer.borderWidth = 1.5 +textfield2.layer.cornerRadius = 5 +textfield1.layer.borderColor = UIColor.blueColor().CGColor +textfield2.placeholder = "Password" +textfield2.textAlignment = NSTextAlignment.Center +subview.addSubview(textfield2) + +// Add the subview to the alert's UI property +alert.customSubview = subview +alert.addButton("Login") { + print("Logged in") +} + +// Add Button with Duration Status and custom Colors +alert.addButton("Duration Button", backgroundColor: UIColor.brownColor(), textColor: UIColor.yellowColor(), showDurationStatus: true) { + print("Duration Button tapped") +} + +alert.showInfo("Login", subTitle: "", duration: 10) +``` + + +#### List of properties to customize + +````swift +// Button +kButtonFont: UIFont +buttonCornerRadius : CGFloat +showCloseButton: Bool +kButtonHeight: CGFloat + +// Circle Image +showCircularIcon: Bool +kCircleTopPosition: CGFloat +kCircleBackgroundTopPosition: CGFloat +kCircleHeight: CGFloat +kCircleIconHeight: CGFloat + +// Text +kTitleFont: UIFont +kTitleTop:CGFloat +kTitleHeight:CGFloat +kTextFont: UIFont +kTextHeight: CGFloat +kTextFieldHeight: CGFloat +kTextViewdHeight: CGFloat + +// View +kDefaultShadowOpacity: CGFloat +kWindowWidth: CGFloat +kWindowHeight: CGFloat +shouldAutoDismiss: Bool // Set this false to 'Disable' Auto hideView when SCLButton is tapped +fieldCornerRadius : CGFloat +contentViewCornerRadius : CGFloat +``` + + +### Alert View Styles + +```swift +enum SCLAlertViewStyle: Int { + case Success, Error, Notice, Warning, Info, Edit, Wait +} +``` + + +### Alert show animation Styles + +```swift +// Animation Styles +public enum SCLAnimationStyle { + case NoAnimation, TopToBottom, BottomToTop, LeftToRight, RightToLeft +} +``` + + +Installation +--- + +SCLAlertView is available through [CocoaPods](http://cocoapods.org). + +To install add the following line to your Podfile: + + pod 'SCLAlertView' + +Collaboration +--- + +I tried to build an easy to use API, while beeing flexible enough for multiple variations, but I'm sure there are ways of improving and adding more features, so feel free to collaborate with ideas, issues and/or pull requests. + +Incoming improvements +--- + +- More animations +- Performance tests + +Has been developed initially for the [Scroll Feed](https://itunes.apple.com/us/app/scroll-feed/id842422195?ls=1&mt=8) app + +- Design [@SherzodMx](https://twitter.com/SherzodMx) Sherzod Max +- Development [@vikmeup](https://twitter.com/vikmeup) Viktor Radchenko +- Improvements by [@bih](http://github.com/bih) Bilawal Hameed, [@rizjoj](http://github.com/rizjoj) Riz Joj diff --git a/Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift b/Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift new file mode 100644 index 0000000..714f41e --- /dev/null +++ b/Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift @@ -0,0 +1,1133 @@ +// +// SCLAlertView.swift +// SCLAlertView Example +// +// Created by Viktor Radchenko on 6/5/14. +// Copyright (c) 2014 Viktor Radchenko. All rights reserved. +// + +import Foundation +import UIKit +fileprivate func < (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l < r + case (nil, _?): + return true + default: + return false + } +} + +fileprivate func > (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l > r + default: + return rhs < lhs + } +} + + +// Pop Up Styles +public enum SCLAlertViewStyle { + case success, error, notice, warning, info, edit, wait + + var defaultColorInt: UInt { + switch self { + case .success: + return 0x22B573 + case .error: + return 0xC1272D + case .notice: + return 0x727375 + case .warning: + return 0xFFD110 + case .info: + return 0x2866BF + case .edit: + return 0xA429FF + case .wait: + return 0xD62DA5 + } + + } + +} + +// Animation Styles +public enum SCLAnimationStyle { + case noAnimation, topToBottom, bottomToTop, leftToRight, rightToLeft +} + +// Action Types +public enum SCLActionType { + case none, selector, closure +} + +// Button sub-class +open class SCLButton: UIButton { + var actionType = SCLActionType.none + var target:AnyObject! + var selector:Selector! + var action:(()->Void)! + var customBackgroundColor:UIColor? + var customTextColor:UIColor? + var initialTitle:String! + var showDurationStatus:Bool=false + + public init() { + super.init(frame: CGRect.zero) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder:aDecoder) + } + + override public init(frame:CGRect) { + super.init(frame:frame) + } +} + +// Allow alerts to be closed/renamed in a chainable manner +// Example: SCLAlertView().showSuccess(self, title: "Test", subTitle: "Value").close() +open class SCLAlertViewResponder { + let alertview: SCLAlertView + + // Initialisation and Title/Subtitle/Close functions + public init(alertview: SCLAlertView) { + self.alertview = alertview + } + + open func setTitle(_ title: String) { + self.alertview.labelTitle.text = title + } + + open func setSubTitle(_ subTitle: String) { + self.alertview.viewText.text = subTitle + } + + open func close() { + self.alertview.hideView() + } + + open func setDismissBlock(_ dismissBlock: @escaping DismissBlock) { + self.alertview.dismissBlock = dismissBlock + } +} + +let kCircleHeightBackground: CGFloat = 62.0 + +public typealias DismissBlock = () -> Void + +// The Main Class +open class SCLAlertView: UIViewController { + + public struct SCLAppearance { + let kDefaultShadowOpacity: CGFloat + let kCircleTopPosition: CGFloat + let kCircleBackgroundTopPosition: CGFloat + let kCircleHeight: CGFloat + let kCircleIconHeight: CGFloat + let kTitleTop:CGFloat + let kTitleHeight:CGFloat + let kWindowWidth: CGFloat + var kWindowHeight: CGFloat + var kTextHeight: CGFloat + let kTextFieldHeight: CGFloat + let kTextViewdHeight: CGFloat + let kButtonHeight: CGFloat + let contentViewColor: UIColor + let contentViewBorderColor: UIColor + let titleColor: UIColor + + // Fonts + let kTitleFont: UIFont + let kTextFont: UIFont + let kButtonFont: UIFont + + // UI Options + var showCloseButton: Bool + var showCircularIcon: Bool + var shouldAutoDismiss: Bool // Set this false to 'Disable' Auto hideView when SCLButton is tapped + var contentViewCornerRadius : CGFloat + var fieldCornerRadius : CGFloat + var buttonCornerRadius : CGFloat + + // Actions + var hideWhenBackgroundViewIsTapped: Bool + + public init(kDefaultShadowOpacity: CGFloat = 0.7, kCircleTopPosition: CGFloat = -12.0, kCircleBackgroundTopPosition: CGFloat = -15.0, kCircleHeight: CGFloat = 56.0, kCircleIconHeight: CGFloat = 20.0, kTitleTop:CGFloat = 30.0, kTitleHeight:CGFloat = 25.0, kWindowWidth: CGFloat = 240.0, kWindowHeight: CGFloat = 178.0, kTextHeight: CGFloat = 90.0, kTextFieldHeight: CGFloat = 45.0, kTextViewdHeight: CGFloat = 80.0, kButtonHeight: CGFloat = 45.0, kTitleFont: UIFont = UIFont.systemFont(ofSize: 20), kTextFont: UIFont = UIFont.systemFont(ofSize: 14), kButtonFont: UIFont = UIFont.boldSystemFont(ofSize: 14), showCloseButton: Bool = true, showCircularIcon: Bool = true, shouldAutoDismiss: Bool = true, contentViewCornerRadius: CGFloat = 5.0, fieldCornerRadius: CGFloat = 3.0, buttonCornerRadius: CGFloat = 3.0, hideWhenBackgroundViewIsTapped: Bool = false, contentViewColor: UIColor = UIColorFromRGB(0xFFFFFF), contentViewBorderColor: UIColor = UIColorFromRGB(0xCCCCCC), titleColor: UIColor = UIColorFromRGB(0x4D4D4D)) { + + self.kDefaultShadowOpacity = kDefaultShadowOpacity + self.kCircleTopPosition = kCircleTopPosition + self.kCircleBackgroundTopPosition = kCircleBackgroundTopPosition + self.kCircleHeight = kCircleHeight + self.kCircleIconHeight = kCircleIconHeight + self.kTitleTop = kTitleTop + self.kTitleHeight = kTitleHeight + self.kWindowWidth = kWindowWidth + self.kWindowHeight = kWindowHeight + self.kTextHeight = kTextHeight + self.kTextFieldHeight = kTextFieldHeight + self.kTextViewdHeight = kTextViewdHeight + self.kButtonHeight = kButtonHeight + self.contentViewColor = contentViewColor + self.contentViewBorderColor = contentViewBorderColor + self.titleColor = titleColor + + self.kTitleFont = kTitleFont + self.kTextFont = kTextFont + self.kButtonFont = kButtonFont + + self.showCloseButton = showCloseButton + self.showCircularIcon = showCircularIcon + self.shouldAutoDismiss = shouldAutoDismiss + self.contentViewCornerRadius = contentViewCornerRadius + self.fieldCornerRadius = fieldCornerRadius + self.buttonCornerRadius = buttonCornerRadius + + self.hideWhenBackgroundViewIsTapped = hideWhenBackgroundViewIsTapped + } + + mutating func setkWindowHeight(_ kWindowHeight:CGFloat) { + self.kWindowHeight = kWindowHeight + } + + mutating func setkTextHeight(_ kTextHeight:CGFloat) { + self.kTextHeight = kTextHeight + } + } + + var appearance: SCLAppearance! + + // UI Colour + var viewColor = UIColor() + + // UI Options + open var iconTintColor: UIColor? + open var customSubview : UIView? + + + + // Members declaration + var baseView = UIView() + var labelTitle = UILabel() + var viewText = UITextView() + var contentView = UIView() + var circleBG = UIView(frame:CGRect(x:0, y:0, width:kCircleHeightBackground, height:kCircleHeightBackground)) + var circleView = UIView() + var circleIconView : UIView? + var duration: TimeInterval! + var durationStatusTimer: Timer! + var durationTimer: Timer! + var dismissBlock : DismissBlock? + fileprivate var inputs = [UITextField]() + fileprivate var input = [UITextView]() + internal var buttons = [SCLButton]() + fileprivate var selfReference: SCLAlertView? + + public init(appearance: SCLAppearance) { + self.appearance = appearance + super.init(nibName:nil, bundle:nil) + setup() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("NSCoding not supported") + } + + required public init() { + appearance = SCLAppearance() + super.init(nibName:nil, bundle:nil) + setup() + } + + override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + appearance = SCLAppearance() + super.init(nibName:nibNameOrNil, bundle:nibBundleOrNil) + } + + fileprivate func setup() { + // Set up main view + view.frame = UIScreen.main.bounds + view.autoresizingMask = [UIViewAutoresizing.flexibleHeight, UIViewAutoresizing.flexibleWidth] + view.backgroundColor = UIColor(red:0, green:0, blue:0, alpha:appearance.kDefaultShadowOpacity) + view.addSubview(baseView) + // Base View + baseView.frame = view.frame + baseView.addSubview(contentView) + // Content View + contentView.layer.cornerRadius = appearance.contentViewCornerRadius + contentView.layer.masksToBounds = true + contentView.layer.borderWidth = 0.5 + contentView.addSubview(labelTitle) + contentView.addSubview(viewText) + // Circle View + circleBG.backgroundColor = UIColor.white + circleBG.layer.cornerRadius = circleBG.frame.size.height / 2 + baseView.addSubview(circleBG) + circleBG.addSubview(circleView) + let x = (kCircleHeightBackground - appearance.kCircleHeight) / 2 + circleView.frame = CGRect(x:x, y:x, width:appearance.kCircleHeight, height:appearance.kCircleHeight) + circleView.layer.cornerRadius = circleView.frame.size.height / 2 + // Title + labelTitle.numberOfLines = 1 + labelTitle.textAlignment = .center + labelTitle.font = appearance.kTitleFont + labelTitle.frame = CGRect(x:12, y:appearance.kTitleTop, width: appearance.kWindowWidth - 24, height:appearance.kTitleHeight) + // View text + viewText.isEditable = false + viewText.textAlignment = .center + viewText.textContainerInset = UIEdgeInsets.zero + viewText.textContainer.lineFragmentPadding = 0; + viewText.font = appearance.kTextFont + // Colours + contentView.backgroundColor = appearance.contentViewColor + viewText.backgroundColor = appearance.contentViewColor + labelTitle.textColor = appearance.titleColor + viewText.textColor = appearance.titleColor + contentView.layer.borderColor = appearance.contentViewBorderColor.cgColor + //Gesture Recognizer for tapping outside the textinput + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SCLAlertView.tapped(_:))) + tapGesture.numberOfTapsRequired = 1 + self.view.addGestureRecognizer(tapGesture) + } + + override open func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + let rv = UIApplication.shared.keyWindow! as UIWindow + let sz = rv.frame.size + + // Set background frame + view.frame.size = sz + + // computing the right size to use for the textView + let maxHeight = sz.height - 100 // max overall height + var consumedHeight = CGFloat(0) + consumedHeight += appearance.kTitleTop + appearance.kTitleHeight + consumedHeight += 14 + consumedHeight += appearance.kButtonHeight * CGFloat(buttons.count) + consumedHeight += appearance.kTextFieldHeight * CGFloat(inputs.count) + consumedHeight += appearance.kTextViewdHeight * CGFloat(input.count) + let maxViewTextHeight = maxHeight - consumedHeight + let viewTextWidth = appearance.kWindowWidth - 24 + var viewTextHeight = appearance.kTextHeight + + // Check if there is a custom subview and add it over the textview + if let customSubview = customSubview { + viewTextHeight = min(customSubview.frame.height, maxViewTextHeight) + viewText.text = "" + viewText.addSubview(customSubview) + } else { + // computing the right size to use for the textView + let suggestedViewTextSize = viewText.sizeThatFits(CGSize(width: viewTextWidth, height: CGFloat.greatestFiniteMagnitude)) + viewTextHeight = min(suggestedViewTextSize.height, maxViewTextHeight) + + // scroll management + if (suggestedViewTextSize.height > maxViewTextHeight) { + viewText.isScrollEnabled = true + } else { + viewText.isScrollEnabled = false + } + } + + let windowHeight = consumedHeight + viewTextHeight + // Set frames + var x = (sz.width - appearance.kWindowWidth) / 2 + var y = (sz.height - windowHeight - (appearance.kCircleHeight / 8)) / 2 + contentView.frame = CGRect(x:x, y:y, width:appearance.kWindowWidth, height:windowHeight) + contentView.layer.cornerRadius = appearance.contentViewCornerRadius + y -= kCircleHeightBackground * 0.6 + x = (sz.width - kCircleHeightBackground) / 2 + circleBG.frame = CGRect(x:x, y:y+6, width:kCircleHeightBackground, height:kCircleHeightBackground) + + //adjust Title frame based on circularIcon show/hide flag + let titleOffset : CGFloat = appearance.showCircularIcon ? 0.0 : -12.0 + labelTitle.frame = labelTitle.frame.offsetBy(dx: 0, dy: titleOffset) + + // Subtitle + y = appearance.kTitleTop + appearance.kTitleHeight + titleOffset + viewText.frame = CGRect(x:12, y:y, width: appearance.kWindowWidth - 24, height:appearance.kTextHeight) + viewText.frame = CGRect(x:12, y:y, width: viewTextWidth, height:viewTextHeight) + // Text fields + y += viewTextHeight + 14.0 + for txt in inputs { + txt.frame = CGRect(x:12, y:y, width:appearance.kWindowWidth - 24, height:30) + txt.layer.cornerRadius = appearance.fieldCornerRadius + y += appearance.kTextFieldHeight + } + for txt in input { + txt.frame = CGRect(x:12, y:y, width:appearance.kWindowWidth - 24, height:70) + //txt.layer.cornerRadius = fieldCornerRadius + y += appearance.kTextViewdHeight + } + // Buttons + for btn in buttons { + btn.frame = CGRect(x:12, y:y, width:appearance.kWindowWidth - 24, height:35) + btn.layer.cornerRadius = appearance.buttonCornerRadius + y += appearance.kButtonHeight + } + } + + override open func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + NotificationCenter.default.addObserver(self, selector: #selector(SCLAlertView.keyboardWillShow(_:)), name:NSNotification.Name.UIKeyboardWillShow, object: nil); + NotificationCenter.default.addObserver(self, selector: #selector(SCLAlertView.keyboardWillHide(_:)), name:NSNotification.Name.UIKeyboardWillHide, object: nil); + } + + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) + } + + override open func touchesEnded(_ touches:Set, with event:UIEvent?) { + if event?.touches(for: view)?.count > 0 { + view.endEditing(true) + } + } + + open func addTextField(_ title:String?=nil)->UITextField { + // Update view height + appearance.setkWindowHeight(appearance.kWindowHeight + appearance.kTextFieldHeight) + // Add text field + let txt = UITextField() + txt.borderStyle = UITextBorderStyle.roundedRect + txt.font = appearance.kTextFont + txt.autocapitalizationType = UITextAutocapitalizationType.words + txt.clearButtonMode = UITextFieldViewMode.whileEditing + txt.layer.masksToBounds = true + txt.layer.borderWidth = 1.0 + if title != nil { + txt.placeholder = title! + } + contentView.addSubview(txt) + inputs.append(txt) + return txt + } + + open func addTextView()->UITextView { + // Update view height + appearance.setkWindowHeight(appearance.kWindowHeight + appearance.kTextViewdHeight) + // Add text view + let txt = UITextView() + // No placeholder with UITextView but you can use KMPlaceholderTextView library + txt.font = appearance.kTextFont + //txt.autocapitalizationType = UITextAutocapitalizationType.Words + //txt.clearButtonMode = UITextFieldViewMode.WhileEditing + txt.layer.masksToBounds = true + txt.layer.borderWidth = 1.0 + contentView.addSubview(txt) + input.append(txt) + return txt + } + + @discardableResult + open func addButton(_ title:String, backgroundColor:UIColor? = nil, textColor:UIColor? = nil, showDurationStatus:Bool=false, action:@escaping ()->Void)->SCLButton { + let btn = addButton(title, backgroundColor: backgroundColor, textColor: textColor, showDurationStatus: showDurationStatus) + btn.actionType = SCLActionType.closure + btn.action = action + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapped(_:)), for:.touchUpInside) + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapDown(_:)), for:[.touchDown, .touchDragEnter]) + btn.addTarget(self, action:#selector(SCLAlertView.buttonRelease(_:)), for:[.touchUpInside, .touchUpOutside, .touchCancel, .touchDragOutside] ) + return btn + } + + @discardableResult + open func addButton(_ title:String, backgroundColor:UIColor? = nil, textColor:UIColor? = nil, showDurationStatus:Bool = false, target:AnyObject, selector:Selector)->SCLButton { + let btn = addButton(title, backgroundColor: backgroundColor, textColor: textColor, showDurationStatus: showDurationStatus) + btn.actionType = SCLActionType.selector + btn.target = target + btn.selector = selector + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapped(_:)), for:.touchUpInside) + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapDown(_:)), for:[.touchDown, .touchDragEnter]) + btn.addTarget(self, action:#selector(SCLAlertView.buttonRelease(_:)), for:[.touchUpInside, .touchUpOutside, .touchCancel, .touchDragOutside] ) + return btn + } + + @discardableResult + fileprivate func addButton(_ title:String, backgroundColor:UIColor? = nil, textColor:UIColor? = nil, showDurationStatus:Bool=false)->SCLButton { + // Update view height + appearance.setkWindowHeight(appearance.kWindowHeight + appearance.kButtonHeight) + // Add button + let btn = SCLButton() + btn.layer.masksToBounds = true + btn.setTitle(title, for: UIControlState()) + btn.titleLabel?.font = appearance.kButtonFont + btn.customBackgroundColor = backgroundColor + btn.customTextColor = textColor + btn.initialTitle = title + btn.showDurationStatus = showDurationStatus + contentView.addSubview(btn) + buttons.append(btn) + return btn + } + + func buttonTapped(_ btn:SCLButton) { + if btn.actionType == SCLActionType.closure { + btn.action() + } else if btn.actionType == SCLActionType.selector { + let ctrl = UIControl() + ctrl.sendAction(btn.selector, to:btn.target, for:nil) + } else { + print("Unknow action type for button") + } + + if(self.view.alpha != 0.0 && appearance.shouldAutoDismiss){ hideView() } + } + + + func buttonTapDown(_ btn:SCLButton) { + var hue : CGFloat = 0 + var saturation : CGFloat = 0 + var brightness : CGFloat = 0 + var alpha : CGFloat = 0 + let pressBrightnessFactor = 0.85 + btn.backgroundColor?.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) + brightness = brightness * CGFloat(pressBrightnessFactor) + btn.backgroundColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) + } + + func buttonRelease(_ btn:SCLButton) { + btn.backgroundColor = btn.customBackgroundColor ?? viewColor + } + + var tmpContentViewFrameOrigin: CGPoint? + var tmpCircleViewFrameOrigin: CGPoint? + var keyboardHasBeenShown:Bool = false + + func keyboardWillShow(_ notification: Notification) { + keyboardHasBeenShown = true + + guard let userInfo = (notification as NSNotification).userInfo else {return} + guard let endKeyBoardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.minY else {return} + + if tmpContentViewFrameOrigin == nil { + tmpContentViewFrameOrigin = self.contentView.frame.origin + } + + if tmpCircleViewFrameOrigin == nil { + tmpCircleViewFrameOrigin = self.circleBG.frame.origin + } + + var newContentViewFrameY = self.contentView.frame.maxY - endKeyBoardFrame + if newContentViewFrameY < 0 { + newContentViewFrameY = 0 + } + let newBallViewFrameY = self.circleBG.frame.origin.y - newContentViewFrameY + self.contentView.frame.origin.y -= newContentViewFrameY + self.circleBG.frame.origin.y = newBallViewFrameY + } + + func keyboardWillHide(_ notification: Notification) { + if(keyboardHasBeenShown){//This could happen on the simulator (keyboard will be hidden) + if(self.tmpContentViewFrameOrigin != nil){ + self.contentView.frame.origin.y = self.tmpContentViewFrameOrigin!.y + self.tmpContentViewFrameOrigin = nil + } + if(self.tmpCircleViewFrameOrigin != nil){ + self.circleBG.frame.origin.y = self.tmpCircleViewFrameOrigin!.y + self.tmpCircleViewFrameOrigin = nil + } + + keyboardHasBeenShown = false + } + } + + //Dismiss keyboard when tapped outside textfield & close SCLAlertView when hideWhenBackgroundViewIsTapped + func tapped(_ gestureRecognizer: UITapGestureRecognizer) { + self.view.endEditing(true) + + if let tappedView = gestureRecognizer.view , tappedView.hitTest(gestureRecognizer.location(in: tappedView), with: nil) == baseView && appearance.hideWhenBackgroundViewIsTapped { + + hideView() + } + } + + // showCustom(view, title, subTitle, UIColor, UIImage) + open func showCustom(_ title: String, subTitle: String, color: UIColor, icon: UIImage, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.success.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + + + var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 + + color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + + var colorAsUInt32 : UInt32 = 0 + colorAsUInt32 += UInt32(red * 255.0) << 16 + colorAsUInt32 += UInt32(green * 255.0) << 8 + colorAsUInt32 += UInt32(blue * 255.0) + + let colorAsUInt = UInt(colorAsUInt32) + + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .success, colorStyle: colorAsUInt, colorTextButton: colorTextButton, circleIconImage: icon, animationStyle: animationStyle) + } + + // showSuccess(view, title, subTitle) + @discardableResult + open func showSuccess(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.success.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .success, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showError(view, title, subTitle) + @discardableResult + open func showError(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.error.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .error, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showNotice(view, title, subTitle) + @discardableResult + open func showNotice(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.notice.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .notice, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showWarning(view, title, subTitle) + @discardableResult + open func showWarning(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.warning.defaultColorInt, colorTextButton: UInt=0x000000, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .warning, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showInfo(view, title, subTitle) + @discardableResult + open func showInfo(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.info.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .info, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showWait(view, title, subTitle) + @discardableResult + open func showWait(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt?=SCLAlertViewStyle.wait.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .wait, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + @discardableResult + open func showEdit(_ title: String, subTitle: String, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt=SCLAlertViewStyle.edit.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, duration: duration, completeText:closeButtonTitle, style: .edit, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showTitle(view, title, subTitle, style) + @discardableResult + open func showTitle(_ title: String, subTitle: String, style: SCLAlertViewStyle, closeButtonTitle:String?=nil, duration:TimeInterval=0.0, colorStyle: UInt?=0x000000, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + + return showTitle(title, subTitle: subTitle, duration:duration, completeText:closeButtonTitle, style: style, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showTitle(view, title, subTitle, duration, style) + @discardableResult + open func showTitle(_ title: String, subTitle: String, duration: TimeInterval?, completeText: String?, style: SCLAlertViewStyle, colorStyle: UInt?=0x000000, colorTextButton: UInt?=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + selfReference = self + view.alpha = 0 + let rv = UIApplication.shared.keyWindow! as UIWindow + rv.addSubview(view) + view.frame = rv.bounds + baseView.frame = rv.bounds + + // Alert colour/icon + viewColor = UIColor() + var iconImage: UIImage? + let colorInt = colorStyle ?? style.defaultColorInt + viewColor = UIColorFromRGB(colorInt) + // Icon style + switch style { + case .success: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage: SCLAlertViewStyleKit.imageOfCheckmark) + + case .error: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage: SCLAlertViewStyleKit.imageOfCross) + + case .notice: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfNotice) + + case .warning: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfWarning) + + case .info: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfInfo) + + case .edit: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfEdit) + + case .wait: + iconImage = nil + } + + // Title + if !title.isEmpty { + self.labelTitle.text = title + } + + // Subtitle + if !subTitle.isEmpty { + viewText.text = subTitle + // Adjust text view size, if necessary + let str = subTitle as NSString + let attr = [NSFontAttributeName:viewText.font ?? UIFont()] + let sz = CGSize(width: appearance.kWindowWidth - 24, height:90) + let r = str.boundingRect(with: sz, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes:attr, context:nil) + let ht = ceil(r.size.height) + if ht < appearance.kTextHeight { + appearance.kWindowHeight -= (appearance.kTextHeight - ht) + appearance.setkTextHeight(ht) + } + } + + // Done button + if appearance.showCloseButton { + _ = addButton(completeText ?? "Done", target:self, selector:#selector(SCLAlertView.hideView)) + } + + //hidden/show circular view based on the ui option + circleView.isHidden = !appearance.showCircularIcon + circleBG.isHidden = !appearance.showCircularIcon + + // Alert view colour and images + circleView.backgroundColor = viewColor + // Spinner / icon + if style == .wait { + let indicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) + indicator.startAnimating() + circleIconView = indicator + } + else { + if let iconTintColor = iconTintColor { + circleIconView = UIImageView(image: iconImage!.withRenderingMode(.alwaysTemplate)) + circleIconView?.tintColor = iconTintColor + } + else { + circleIconView = UIImageView(image: iconImage!) + } + } + circleView.addSubview(circleIconView!) + let x = (appearance.kCircleHeight - appearance.kCircleIconHeight) / 2 + circleIconView!.frame = CGRect( x: x, y: x, width: appearance.kCircleIconHeight, height: appearance.kCircleIconHeight) + circleIconView?.layer.cornerRadius = circleIconView!.bounds.height / 2 + circleIconView?.layer.masksToBounds = true + + for txt in inputs { + txt.layer.borderColor = viewColor.cgColor + } + + for txt in input { + txt.layer.borderColor = viewColor.cgColor + } + + for btn in buttons { + if let customBackgroundColor = btn.customBackgroundColor { + // Custom BackgroundColor set + btn.backgroundColor = customBackgroundColor + } else { + // Use default BackgroundColor derived from AlertStyle + btn.backgroundColor = viewColor + } + + if let customTextColor = btn.customTextColor { + // Custom TextColor set + btn.setTitleColor(customTextColor, for:UIControlState()) + } else { + // Use default BackgroundColor derived from AlertStyle + btn.setTitleColor(UIColorFromRGB(colorTextButton ?? 0xFFFFFF), for:UIControlState()) + } + } + + // Adding duration + if duration > 0 { + self.duration = duration + durationTimer?.invalidate() + durationTimer = Timer.scheduledTimer(timeInterval: self.duration, target: self, selector: #selector(SCLAlertView.hideView), userInfo: nil, repeats: false) + durationStatusTimer?.invalidate() + durationStatusTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(SCLAlertView.updateDurationStatus), userInfo: nil, repeats: true) + } + + // Animate in the alert view + self.showAnimation(animationStyle) + + // Chainable objects + return SCLAlertViewResponder(alertview: self) + } + + // Show animation in the alert view + fileprivate func showAnimation(_ animationStyle: SCLAnimationStyle = .topToBottom, animationStartOffset: CGFloat = -400.0, boundingAnimationOffset: CGFloat = 15.0, animationDuration: TimeInterval = 0.2) { + + let rv = UIApplication.shared.keyWindow! as UIWindow + var animationStartOrigin = self.baseView.frame.origin + var animationCenter : CGPoint = rv.center + + switch animationStyle { + + case .noAnimation: + self.view.alpha = 1.0 + return; + + case .topToBottom: + animationStartOrigin = CGPoint(x: animationStartOrigin.x, y: self.baseView.frame.origin.y + animationStartOffset) + animationCenter = CGPoint(x: animationCenter.x, y: animationCenter.y + boundingAnimationOffset) + + case .bottomToTop: + animationStartOrigin = CGPoint(x: animationStartOrigin.x, y: self.baseView.frame.origin.y - animationStartOffset) + animationCenter = CGPoint(x: animationCenter.x, y: animationCenter.y - boundingAnimationOffset) + + case .leftToRight: + animationStartOrigin = CGPoint(x: self.baseView.frame.origin.x + animationStartOffset, y: animationStartOrigin.y) + animationCenter = CGPoint(x: animationCenter.x + boundingAnimationOffset, y: animationCenter.y) + + case .rightToLeft: + animationStartOrigin = CGPoint(x: self.baseView.frame.origin.x - animationStartOffset, y: animationStartOrigin.y) + animationCenter = CGPoint(x: animationCenter.x - boundingAnimationOffset, y: animationCenter.y) + } + + self.baseView.frame.origin = animationStartOrigin + UIView.animate(withDuration: animationDuration, animations: { + self.view.alpha = 1.0 + self.baseView.center = animationCenter + }, completion: { finished in + UIView.animate(withDuration: animationDuration, animations: { + self.view.alpha = 1.0 + self.baseView.center = rv.center + }) + }) + } + + open func updateDurationStatus() { + duration = duration.advanced(by: -1) + for btn in buttons.filter({$0.showDurationStatus}) { + let txt = "\(btn.initialTitle) (\(duration))" + btn.setTitle(txt, for: UIControlState()) + } + } + + // Close SCLAlertView + open func hideView() { + UIView.animate(withDuration: 0.2, animations: { + self.view.alpha = 0 + }, completion: { finished in + + //Stop durationTimer so alertView does not attempt to hide itself and fire it's dimiss block a second time when close button is tapped + self.durationTimer?.invalidate() + // Stop StatusTimer + self.durationStatusTimer?.invalidate() + + if(self.dismissBlock != nil) { + // Call completion handler when the alert is dismissed + self.dismissBlock!() + } + + // This is necessary for SCLAlertView to be de-initialized, preventing a strong reference cycle with the viewcontroller calling SCLAlertView. + for button in self.buttons { + button.action = nil + button.target = nil + button.selector = nil + } + + self.view.removeFromSuperview() + self.selfReference = nil + }) + } + + func checkCircleIconImage(_ circleIconImage: UIImage?, defaultImage: UIImage) -> UIImage { + if let image = circleIconImage { + return image + } else { + return defaultImage + } + } +} + +// Helper function to convert from RGB to UIColor +func UIColorFromRGB(_ rgbValue: UInt) -> UIColor { + return UIColor( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) +} + +// ------------------------------------ +// Icon drawing +// Code generated by PaintCode +// ------------------------------------ + +class SCLAlertViewStyleKit : NSObject { + + // Cache + struct Cache { + static var imageOfCheckmark: UIImage? + static var checkmarkTargets: [AnyObject]? + static var imageOfCross: UIImage? + static var crossTargets: [AnyObject]? + static var imageOfNotice: UIImage? + static var noticeTargets: [AnyObject]? + static var imageOfWarning: UIImage? + static var warningTargets: [AnyObject]? + static var imageOfInfo: UIImage? + static var infoTargets: [AnyObject]? + static var imageOfEdit: UIImage? + static var editTargets: [AnyObject]? + } + + // Initialization + /// swift 1.2 abolish func load + // override class func load() { + // } + + // Drawing Methods + class func drawCheckmark() { + // Checkmark Shape Drawing + let checkmarkShapePath = UIBezierPath() + checkmarkShapePath.move(to: CGPoint(x: 73.25, y: 14.05)) + checkmarkShapePath.addCurve(to: CGPoint(x: 64.51, y: 13.86), controlPoint1: CGPoint(x: 70.98, y: 11.44), controlPoint2: CGPoint(x: 66.78, y: 11.26)) + checkmarkShapePath.addLine(to: CGPoint(x: 27.46, y: 52)) + checkmarkShapePath.addLine(to: CGPoint(x: 15.75, y: 39.54)) + checkmarkShapePath.addCurve(to: CGPoint(x: 6.84, y: 39.54), controlPoint1: CGPoint(x: 13.48, y: 36.93), controlPoint2: CGPoint(x: 9.28, y: 36.93)) + checkmarkShapePath.addCurve(to: CGPoint(x: 6.84, y: 49.02), controlPoint1: CGPoint(x: 4.39, y: 42.14), controlPoint2: CGPoint(x: 4.39, y: 46.42)) + checkmarkShapePath.addLine(to: CGPoint(x: 22.91, y: 66.14)) + checkmarkShapePath.addCurve(to: CGPoint(x: 27.28, y: 68), controlPoint1: CGPoint(x: 24.14, y: 67.44), controlPoint2: CGPoint(x: 25.71, y: 68)) + checkmarkShapePath.addCurve(to: CGPoint(x: 31.65, y: 66.14), controlPoint1: CGPoint(x: 28.86, y: 68), controlPoint2: CGPoint(x: 30.43, y: 67.26)) + checkmarkShapePath.addLine(to: CGPoint(x: 73.08, y: 23.35)) + checkmarkShapePath.addCurve(to: CGPoint(x: 73.25, y: 14.05), controlPoint1: CGPoint(x: 75.52, y: 20.75), controlPoint2: CGPoint(x: 75.7, y: 16.65)) + checkmarkShapePath.close() + checkmarkShapePath.miterLimit = 4; + + UIColor.white.setFill() + checkmarkShapePath.fill() + } + + class func drawCross() { + // Cross Shape Drawing + let crossShapePath = UIBezierPath() + crossShapePath.move(to: CGPoint(x: 10, y: 70)) + crossShapePath.addLine(to: CGPoint(x: 70, y: 10)) + crossShapePath.move(to: CGPoint(x: 10, y: 10)) + crossShapePath.addLine(to: CGPoint(x: 70, y: 70)) + crossShapePath.lineCapStyle = CGLineCap.round; + crossShapePath.lineJoinStyle = CGLineJoin.round; + UIColor.white.setStroke() + crossShapePath.lineWidth = 14 + crossShapePath.stroke() + } + + class func drawNotice() { + // Notice Shape Drawing + let noticeShapePath = UIBezierPath() + noticeShapePath.move(to: CGPoint(x: 72, y: 48.54)) + noticeShapePath.addLine(to: CGPoint(x: 72, y: 39.9)) + noticeShapePath.addCurve(to: CGPoint(x: 66.38, y: 34.01), controlPoint1: CGPoint(x: 72, y: 36.76), controlPoint2: CGPoint(x: 69.48, y: 34.01)) + noticeShapePath.addCurve(to: CGPoint(x: 61.53, y: 35.97), controlPoint1: CGPoint(x: 64.82, y: 34.01), controlPoint2: CGPoint(x: 62.69, y: 34.8)) + noticeShapePath.addCurve(to: CGPoint(x: 60.36, y: 35.78), controlPoint1: CGPoint(x: 61.33, y: 35.97), controlPoint2: CGPoint(x: 62.3, y: 35.78)) + noticeShapePath.addLine(to: CGPoint(x: 60.36, y: 33.22)) + noticeShapePath.addCurve(to: CGPoint(x: 54.16, y: 26.16), controlPoint1: CGPoint(x: 60.36, y: 29.3), controlPoint2: CGPoint(x: 57.65, y: 26.16)) + noticeShapePath.addCurve(to: CGPoint(x: 48.73, y: 29.89), controlPoint1: CGPoint(x: 51.64, y: 26.16), controlPoint2: CGPoint(x: 50.67, y: 27.73)) + noticeShapePath.addLine(to: CGPoint(x: 48.73, y: 28.71)) + noticeShapePath.addCurve(to: CGPoint(x: 43.49, y: 21.64), controlPoint1: CGPoint(x: 48.73, y: 24.78), controlPoint2: CGPoint(x: 46.98, y: 21.64)) + noticeShapePath.addCurve(to: CGPoint(x: 39.03, y: 25.37), controlPoint1: CGPoint(x: 40.97, y: 21.64), controlPoint2: CGPoint(x: 39.03, y: 23.01)) + noticeShapePath.addLine(to: CGPoint(x: 39.03, y: 9.07)) + noticeShapePath.addCurve(to: CGPoint(x: 32.24, y: 2), controlPoint1: CGPoint(x: 39.03, y: 5.14), controlPoint2: CGPoint(x: 35.73, y: 2)) + noticeShapePath.addCurve(to: CGPoint(x: 25.45, y: 9.07), controlPoint1: CGPoint(x: 28.56, y: 2), controlPoint2: CGPoint(x: 25.45, y: 5.14)) + noticeShapePath.addLine(to: CGPoint(x: 25.45, y: 41.47)) + noticeShapePath.addCurve(to: CGPoint(x: 24.29, y: 43.44), controlPoint1: CGPoint(x: 25.45, y: 42.45), controlPoint2: CGPoint(x: 24.68, y: 43.04)) + noticeShapePath.addCurve(to: CGPoint(x: 9.55, y: 43.04), controlPoint1: CGPoint(x: 16.73, y: 40.88), controlPoint2: CGPoint(x: 11.88, y: 40.69)) + noticeShapePath.addCurve(to: CGPoint(x: 8, y: 46.58), controlPoint1: CGPoint(x: 8.58, y: 43.83), controlPoint2: CGPoint(x: 8, y: 45.2)) + noticeShapePath.addCurve(to: CGPoint(x: 14.4, y: 55.81), controlPoint1: CGPoint(x: 8.19, y: 50.31), controlPoint2: CGPoint(x: 12.07, y: 53.84)) + noticeShapePath.addLine(to: CGPoint(x: 27.2, y: 69.56)) + noticeShapePath.addCurve(to: CGPoint(x: 42.91, y: 77.8), controlPoint1: CGPoint(x: 30.5, y: 74.47), controlPoint2: CGPoint(x: 35.73, y: 77.21)) + noticeShapePath.addCurve(to: CGPoint(x: 43.88, y: 77.8), controlPoint1: CGPoint(x: 43.3, y: 77.8), controlPoint2: CGPoint(x: 43.68, y: 77.8)) + noticeShapePath.addCurve(to: CGPoint(x: 47.18, y: 78), controlPoint1: CGPoint(x: 45.04, y: 77.8), controlPoint2: CGPoint(x: 46.01, y: 78)) + noticeShapePath.addLine(to: CGPoint(x: 48.34, y: 78)) + noticeShapePath.addLine(to: CGPoint(x: 48.34, y: 78)) + noticeShapePath.addCurve(to: CGPoint(x: 71.61, y: 52.08), controlPoint1: CGPoint(x: 56.48, y: 78), controlPoint2: CGPoint(x: 69.87, y: 75.05)) + noticeShapePath.addCurve(to: CGPoint(x: 72, y: 48.54), controlPoint1: CGPoint(x: 71.81, y: 51.29), controlPoint2: CGPoint(x: 72, y: 49.72)) + noticeShapePath.close() + noticeShapePath.miterLimit = 4; + + UIColor.white.setFill() + noticeShapePath.fill() + } + + class func drawWarning() { + // Color Declarations + let greyColor = UIColor(red: 0.236, green: 0.236, blue: 0.236, alpha: 1.000) + + // Warning Group + // Warning Circle Drawing + let warningCirclePath = UIBezierPath() + warningCirclePath.move(to: CGPoint(x: 40.94, y: 63.39)) + warningCirclePath.addCurve(to: CGPoint(x: 36.03, y: 65.55), controlPoint1: CGPoint(x: 39.06, y: 63.39), controlPoint2: CGPoint(x: 37.36, y: 64.18)) + warningCirclePath.addCurve(to: CGPoint(x: 34.14, y: 70.45), controlPoint1: CGPoint(x: 34.9, y: 66.92), controlPoint2: CGPoint(x: 34.14, y: 68.49)) + warningCirclePath.addCurve(to: CGPoint(x: 36.22, y: 75.54), controlPoint1: CGPoint(x: 34.14, y: 72.41), controlPoint2: CGPoint(x: 34.9, y: 74.17)) + warningCirclePath.addCurve(to: CGPoint(x: 40.94, y: 77.5), controlPoint1: CGPoint(x: 37.54, y: 76.91), controlPoint2: CGPoint(x: 39.06, y: 77.5)) + warningCirclePath.addCurve(to: CGPoint(x: 45.86, y: 75.35), controlPoint1: CGPoint(x: 42.83, y: 77.5), controlPoint2: CGPoint(x: 44.53, y: 76.72)) + warningCirclePath.addCurve(to: CGPoint(x: 47.93, y: 70.45), controlPoint1: CGPoint(x: 47.18, y: 74.17), controlPoint2: CGPoint(x: 47.93, y: 72.41)) + warningCirclePath.addCurve(to: CGPoint(x: 45.86, y: 65.35), controlPoint1: CGPoint(x: 47.93, y: 68.49), controlPoint2: CGPoint(x: 47.18, y: 66.72)) + warningCirclePath.addCurve(to: CGPoint(x: 40.94, y: 63.39), controlPoint1: CGPoint(x: 44.53, y: 64.18), controlPoint2: CGPoint(x: 42.83, y: 63.39)) + warningCirclePath.close() + warningCirclePath.miterLimit = 4; + + greyColor.setFill() + warningCirclePath.fill() + + + // Warning Shape Drawing + let warningShapePath = UIBezierPath() + warningShapePath.move(to: CGPoint(x: 46.23, y: 4.26)) + warningShapePath.addCurve(to: CGPoint(x: 40.94, y: 2.5), controlPoint1: CGPoint(x: 44.91, y: 3.09), controlPoint2: CGPoint(x: 43.02, y: 2.5)) + warningShapePath.addCurve(to: CGPoint(x: 34.71, y: 4.26), controlPoint1: CGPoint(x: 38.68, y: 2.5), controlPoint2: CGPoint(x: 36.03, y: 3.09)) + warningShapePath.addCurve(to: CGPoint(x: 31.5, y: 8.77), controlPoint1: CGPoint(x: 33.01, y: 5.44), controlPoint2: CGPoint(x: 31.5, y: 7.01)) + warningShapePath.addLine(to: CGPoint(x: 31.5, y: 19.36)) + warningShapePath.addLine(to: CGPoint(x: 34.71, y: 54.44)) + warningShapePath.addCurve(to: CGPoint(x: 40.38, y: 58.16), controlPoint1: CGPoint(x: 34.9, y: 56.2), controlPoint2: CGPoint(x: 36.41, y: 58.16)) + warningShapePath.addCurve(to: CGPoint(x: 45.67, y: 54.44), controlPoint1: CGPoint(x: 44.34, y: 58.16), controlPoint2: CGPoint(x: 45.67, y: 56.01)) + warningShapePath.addLine(to: CGPoint(x: 48.5, y: 19.36)) + warningShapePath.addLine(to: CGPoint(x: 48.5, y: 8.77)) + warningShapePath.addCurve(to: CGPoint(x: 46.23, y: 4.26), controlPoint1: CGPoint(x: 48.5, y: 7.01), controlPoint2: CGPoint(x: 47.74, y: 5.44)) + warningShapePath.close() + warningShapePath.miterLimit = 4; + + greyColor.setFill() + warningShapePath.fill() + } + + class func drawInfo() { + // Color Declarations + let color0 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000) + + // Info Shape Drawing + let infoShapePath = UIBezierPath() + infoShapePath.move(to: CGPoint(x: 45.66, y: 15.96)) + infoShapePath.addCurve(to: CGPoint(x: 45.66, y: 5.22), controlPoint1: CGPoint(x: 48.78, y: 12.99), controlPoint2: CGPoint(x: 48.78, y: 8.19)) + infoShapePath.addCurve(to: CGPoint(x: 34.34, y: 5.22), controlPoint1: CGPoint(x: 42.53, y: 2.26), controlPoint2: CGPoint(x: 37.47, y: 2.26)) + infoShapePath.addCurve(to: CGPoint(x: 34.34, y: 15.96), controlPoint1: CGPoint(x: 31.22, y: 8.19), controlPoint2: CGPoint(x: 31.22, y: 12.99)) + infoShapePath.addCurve(to: CGPoint(x: 45.66, y: 15.96), controlPoint1: CGPoint(x: 37.47, y: 18.92), controlPoint2: CGPoint(x: 42.53, y: 18.92)) + infoShapePath.close() + infoShapePath.move(to: CGPoint(x: 48, y: 69.41)) + infoShapePath.addCurve(to: CGPoint(x: 40, y: 77), controlPoint1: CGPoint(x: 48, y: 73.58), controlPoint2: CGPoint(x: 44.4, y: 77)) + infoShapePath.addLine(to: CGPoint(x: 40, y: 77)) + infoShapePath.addCurve(to: CGPoint(x: 32, y: 69.41), controlPoint1: CGPoint(x: 35.6, y: 77), controlPoint2: CGPoint(x: 32, y: 73.58)) + infoShapePath.addLine(to: CGPoint(x: 32, y: 35.26)) + infoShapePath.addCurve(to: CGPoint(x: 40, y: 27.67), controlPoint1: CGPoint(x: 32, y: 31.08), controlPoint2: CGPoint(x: 35.6, y: 27.67)) + infoShapePath.addLine(to: CGPoint(x: 40, y: 27.67)) + infoShapePath.addCurve(to: CGPoint(x: 48, y: 35.26), controlPoint1: CGPoint(x: 44.4, y: 27.67), controlPoint2: CGPoint(x: 48, y: 31.08)) + infoShapePath.addLine(to: CGPoint(x: 48, y: 69.41)) + infoShapePath.close() + color0.setFill() + infoShapePath.fill() + } + + class func drawEdit() { + // Color Declarations + let color = UIColor(red:1.0, green:1.0, blue:1.0, alpha:1.0) + + // Edit shape Drawing + let editPathPath = UIBezierPath() + editPathPath.move(to: CGPoint(x: 71, y: 2.7)) + editPathPath.addCurve(to: CGPoint(x: 71.9, y: 15.2), controlPoint1: CGPoint(x: 74.7, y: 5.9), controlPoint2: CGPoint(x: 75.1, y: 11.6)) + editPathPath.addLine(to: CGPoint(x: 64.5, y: 23.7)) + editPathPath.addLine(to: CGPoint(x: 49.9, y: 11.1)) + editPathPath.addLine(to: CGPoint(x: 57.3, y: 2.6)) + editPathPath.addCurve(to: CGPoint(x: 69.7, y: 1.7), controlPoint1: CGPoint(x: 60.4, y: -1.1), controlPoint2: CGPoint(x: 66.1, y: -1.5)) + editPathPath.addLine(to: CGPoint(x: 71, y: 2.7)) + editPathPath.addLine(to: CGPoint(x: 71, y: 2.7)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 47.8, y: 13.5)) + editPathPath.addLine(to: CGPoint(x: 13.4, y: 53.1)) + editPathPath.addLine(to: CGPoint(x: 15.7, y: 55.1)) + editPathPath.addLine(to: CGPoint(x: 50.1, y: 15.5)) + editPathPath.addLine(to: CGPoint(x: 47.8, y: 13.5)) + editPathPath.addLine(to: CGPoint(x: 47.8, y: 13.5)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 17.7, y: 56.7)) + editPathPath.addLine(to: CGPoint(x: 23.8, y: 62.2)) + editPathPath.addLine(to: CGPoint(x: 58.2, y: 22.6)) + editPathPath.addLine(to: CGPoint(x: 52, y: 17.1)) + editPathPath.addLine(to: CGPoint(x: 17.7, y: 56.7)) + editPathPath.addLine(to: CGPoint(x: 17.7, y: 56.7)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 25.8, y: 63.8)) + editPathPath.addLine(to: CGPoint(x: 60.1, y: 24.2)) + editPathPath.addLine(to: CGPoint(x: 62.3, y: 26.1)) + editPathPath.addLine(to: CGPoint(x: 28.1, y: 65.7)) + editPathPath.addLine(to: CGPoint(x: 25.8, y: 63.8)) + editPathPath.addLine(to: CGPoint(x: 25.8, y: 63.8)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 25.9, y: 68.1)) + editPathPath.addLine(to: CGPoint(x: 4.2, y: 79.5)) + editPathPath.addLine(to: CGPoint(x: 11.3, y: 55.5)) + editPathPath.addLine(to: CGPoint(x: 25.9, y: 68.1)) + editPathPath.close() + editPathPath.miterLimit = 4; + editPathPath.usesEvenOddFillRule = true; + color.setFill() + editPathPath.fill() + } + + // Generated Images + class var imageOfCheckmark: UIImage { + if (Cache.imageOfCheckmark != nil) { + return Cache.imageOfCheckmark! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawCheckmark() + Cache.imageOfCheckmark = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfCheckmark! + } + + class var imageOfCross: UIImage { + if (Cache.imageOfCross != nil) { + return Cache.imageOfCross! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawCross() + Cache.imageOfCross = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfCross! + } + + class var imageOfNotice: UIImage { + if (Cache.imageOfNotice != nil) { + return Cache.imageOfNotice! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawNotice() + Cache.imageOfNotice = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfNotice! + } + + class var imageOfWarning: UIImage { + if (Cache.imageOfWarning != nil) { + return Cache.imageOfWarning! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawWarning() + Cache.imageOfWarning = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfWarning! + } + + class var imageOfInfo: UIImage { + if (Cache.imageOfInfo != nil) { + return Cache.imageOfInfo! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawInfo() + Cache.imageOfInfo = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfInfo! + } + + class var imageOfEdit: UIImage { + if (Cache.imageOfEdit != nil) { + return Cache.imageOfEdit! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawEdit() + Cache.imageOfEdit = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfEdit! + } +} diff --git a/Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift b/Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift new file mode 100644 index 0000000..58ebb95 --- /dev/null +++ b/Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift @@ -0,0 +1,41 @@ +// +// SCLExtensions.swift +// SCLAlertView +// +// Created by Christian Cabarrocas on 16/04/16. +// Copyright © 2016 Alexey Poimtsev. All rights reserved. +// + +import UIKit + +extension Int { + + func toUIColor() -> UIColor { + return UIColor( + red: CGFloat((self & 0xFF0000) >> 16) / 255.0, + green: CGFloat((self & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(self & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } + + func toCGColor() -> CGColor { + return self.toUIColor().cgColor + } +} + +extension UInt { + + func toUIColor() -> UIColor { + return UIColor( + red: CGFloat((self & 0xFF0000) >> 16) / 255.0, + green: CGFloat((self & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(self & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } + + func toCGColor() -> CGColor { + return self.toUIColor().cgColor + } +} diff --git a/Pods/SwiftOverlays/LICENSE b/Pods/SwiftOverlays/LICENSE new file mode 100644 index 0000000..4c57bb4 --- /dev/null +++ b/Pods/SwiftOverlays/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Peter Prokop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Pods/SwiftOverlays/README.md b/Pods/SwiftOverlays/README.md new file mode 100644 index 0000000..8d0626a --- /dev/null +++ b/Pods/SwiftOverlays/README.md @@ -0,0 +1,122 @@ +[![Build Status](https://travis-ci.org/peterprokop/SwiftOverlays.svg?branch=master)](https://travis-ci.org/peterprokop/SwiftOverlays) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +# SwiftOverlays + +SwiftOverlays is a Swift GUI library for displaying various popups and notifications. + + +## Features + +SwiftOverlays provides several ways to notify user: + +- [x] Wait overlay: a simple overlay with activity indicator + +![Wait](https://i.imgflip.com/df53v.gif) + +- [x] Wait overlay with text + +![WaitWithText](https://i.imgflip.com/df525.gif) + +- [x] Overlay with text only +- [x] Overlay with image and text (can be used with [PPSwiftGifs](https://github.com/peterprokop/PPSwiftGifs) to show custom animated GIF instead of UIActivityIndicatorView) +- [x] All of the above with blocking any user interaction +- [x] Notification on top of the status bar, similar to native iOS local/push notifications + +![Notification](https://i.imgflip.com/df5k5.gif) + +## Installation + +### Manual +Just clone and add ```SwiftOverlays.swift``` to your project. + +### Carthage +* `> Cartfile` +* `nano Cartfile` +* put `github "peterprokop/SwiftOverlays" ~> 2.0.0` into Cartfile +* Save it: `ctrl-x`, `y`, `enter` +* Run `carthage update` +* Copy `SwiftOverlays.framework` from `Carthage/Build/iOS` to your project +* Make sure that `SwiftOverlays` is added in `Embedded Binaries` section of your target (or else you will get `dyld library not loaded referenced from ... reason image not found` error) +* Add `import SwiftOverlays` on top of your view controller's code + +### Cocoapods +- Make sure that you use latest stable Cocoapods version: `pod --version` +- If not, update it: `sudo gem install cocoapods` +- `pod init` in you project root dir +- `nano Podfile`, add: + +``` +pod 'SwiftOverlays', '~> 2.0.0' +use_frameworks! +``` +- Save it: `ctrl-x`, `y`, `enter` +- `pod update` +- Open generated `.xcworkspace` +- Don't forget to import SwiftOverlays: `import SwiftOverlays`! + +## Requirements + +- iOS 7.0+ (8.0+ if you use Cocoapods) +- Xcode 8.0+ +- Swift 3.0 (if you need older swift version, see following branches: [swift-1.1](https://github.com/peterprokop/SwiftOverlays/tree/swift-1.1), [swift-1.2](https://github.com/peterprokop/SwiftOverlays/tree/swift-1.2), +[swift-2.1](https://github.com/peterprokop/SwiftOverlays/tree/swift-2.1) and others) + +## Usage + +If you're using CocoaPods, import the library with `import SwiftOverlays` + +You can use UIViewController convenience methods provided by library: + +```swift +// In your view controller: + +// Wait overlay +self.showWaitOverlay() + +// Wait overlay with text +let text = "Please wait..." +self.showWaitOverlayWithText(text) + +// Overlay with text only +let text = "This is a text-only overlay...\n...spanning several lines" +self.showTextOverlay(text) + +// Remove everything +self.removeAllOverlays() + +// Notification on top of the status bar +UIViewController.showNotificationOnTopOfStatusBar(annoyingNotificationView!, duration: 5) + +// Block user interaction +SwiftOverlays.showBlockingWaitOverlayWithText("This is blocking overlay!") + +// Don't forget to unblock! +SwiftOverlays.removeAllBlockingOverlays() + +``` + +### Using with UITableViewController/UICollectionViewController + +You can't use SwiftOverlays convenience methods directly with UITableViewController - because its view is, well, an UITableView, and overlay will be scrolled along with it. + +Instead I suggest using UIViewController instead of UITableViewController and adding UITableView as a subview. +(the same applies to UICollectionViewController) + +If for some reason you can't use UIViewController, you can do something like: +```swift +if let superview = self.view.superview { + SwiftOverlays.showCenteredWaitOverlayWithText(superview, text: "Please wait...") + SwiftOverlays.removeAllOverlaysFromView(superview) +} +``` + +(but in that case overlay will be added to the superview, and you should obviously do that only if superview is available - for example in viewDidAppear method of your controller.). + +## Contribution + +You are welcome to fork and submit pull requests + +## Other Projects + +[StarryStars](https://github.com/peterprokop/StarryStars) - iOS GUI library for displaying and editing ratings. diff --git a/Pods/SwiftOverlays/SwiftOverlays/SwiftOverlays.swift b/Pods/SwiftOverlays/SwiftOverlays/SwiftOverlays.swift new file mode 100644 index 0000000..d769a73 --- /dev/null +++ b/Pods/SwiftOverlays/SwiftOverlays/SwiftOverlays.swift @@ -0,0 +1,528 @@ +// +// SwiftOverlays.swift +// SwiftTest +// +// Created by Peter Prokop on 15/10/14. +// Copyright (c) 2014 Peter Prokop. All rights reserved. +// + +import Foundation +import UIKit + + +// For convenience methods +public extension UIViewController { + + /** + Shows wait overlay with activity indicator, centered in the view controller's main view + + Do not use this method for **UITableViewController** or **UICollectionViewController** + + - returns: Created overlay + */ + @discardableResult + func showWaitOverlay() -> UIView { + return SwiftOverlays.showCenteredWaitOverlay(self.view) + } + + /** + Shows wait overlay with activity indicator *and text*, centered in the view controller's main view + + Do not use this method for **UITableViewController** or **UICollectionViewController** + + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + @discardableResult + func showWaitOverlayWithText(_ text: String) -> UIView { + return SwiftOverlays.showCenteredWaitOverlayWithText(self.view, text: text) + } + + /** + Shows *text-only* overlay, centered in the view controller's main view + + Do not use this method for **UITableViewController** or **UICollectionViewController** + + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + @discardableResult + func showTextOverlay(_ text: String) -> UIView { + return SwiftOverlays.showTextOverlay(self.view, text: text) + } + + /** + Shows overlay with text and progress bar, centered in the view controller's main view + + Do not use this method for **UITableViewController** or **UICollectionViewController** + + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + @discardableResult + func showProgressOverlay(_ text: String) -> UIView { + return SwiftOverlays.showProgressOverlay(self.view, text: text) + } + + /** + Shows overlay *with image and text*, centered in the view controller's main view + + Do not use this method for **UITableViewController** or **UICollectionViewController** + + - parameter image: Image to be added to overlay + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + @discardableResult + func showImageAndTextOverlay(_ image: UIImage, text: String) -> UIView { + return SwiftOverlays.showImageAndTextOverlay(self.view, image: image, text: text) + } + + /** + Shows notification on top of the status bar, similar to native local or remote notifications + + - parameter notificationView: View that will be shown as notification + - parameter duration: Amount of time until notification disappears + - parameter animated: Should appearing be animated + */ + @discardableResult + class func showNotificationOnTopOfStatusBar(_ notificationView: UIView, duration: TimeInterval, animated: Bool = true) { + SwiftOverlays.showAnnoyingNotificationOnTopOfStatusBar(notificationView, duration: duration, animated: animated) + } + + /** + Removes all overlays from view controller's main view + */ + func removeAllOverlays() { + SwiftOverlays.removeAllOverlaysFromView(self.view) + } + + /** + Updates text on the current overlay. + Does nothing if no overlay is present. + + - parameter text: Text to set + */ + func updateOverlayText(_ text: String) { + SwiftOverlays.updateOverlayText(self.view, text: text) + } + + /** + Updates progress on the current overlay. + Does nothing if no overlay is present. + + - parameter progress: Progress to set 0.0 .. 1.0 + */ + func updateOverlayProgress(_ progress: Float) { + SwiftOverlays.updateOverlayProgress(self.view, progress: progress) + } +} + +open class SwiftOverlays: NSObject { + // You can customize these values + + // Some random number + static let containerViewTag = 456987123 + + static let cornerRadius = CGFloat(10) + static let padding = CGFloat(10) + + static let backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.7) + static let textColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1) + static let font = UIFont.systemFont(ofSize: 14) + + // Annoying notifications on top of status bar + static let bannerDissapearAnimationDuration = 0.5 + + static var bannerWindow : UIWindow? + + open class Utils { + + /** + Adds autolayout constraints to innerView to center it in its superview and fix its size. + `innerView` should have a superview. + + - parameter innerView: View to set constraints on + */ + open static func centerViewInSuperview(_ view: UIView) { + assert(view.superview != nil, "`view` should have a superview") + + view.translatesAutoresizingMaskIntoConstraints = false + + let constraintH = NSLayoutConstraint(item: view, + attribute: NSLayoutAttribute.centerX, + relatedBy: NSLayoutRelation.equal, + toItem: view.superview, + attribute: NSLayoutAttribute.centerX, + multiplier: 1, + constant: 0) + let constraintV = NSLayoutConstraint(item: view, + attribute: NSLayoutAttribute.centerY, + relatedBy: NSLayoutRelation.equal, + toItem: view.superview, + attribute: NSLayoutAttribute.centerY, + multiplier: 1, + constant: 0) + let constraintWidth = NSLayoutConstraint(item: view, + attribute: NSLayoutAttribute.width, + relatedBy: NSLayoutRelation.equal, + toItem: nil, + attribute: NSLayoutAttribute.notAnAttribute, + multiplier: 1, + constant: view.frame.size.width) + let constraintHeight = NSLayoutConstraint(item: view, + attribute: NSLayoutAttribute.height, + relatedBy: NSLayoutRelation.equal, + toItem: nil, + attribute: NSLayoutAttribute.notAnAttribute, + multiplier: 1, + constant: view.frame.size.height) + view.superview!.addConstraints([constraintV, constraintH, constraintWidth, constraintHeight]) + } + } + + // MARK: - Public class methods - + + // MARK: Blocking + + /** + Shows *blocking* wait overlay with activity indicator, centered in the app's main window + + - returns: Created overlay + */ + @discardableResult + open class func showBlockingWaitOverlay() -> UIView { + let blocker = addMainWindowBlocker() + showCenteredWaitOverlay(blocker) + + return blocker + } + + /** + Shows wait overlay with activity indicator *and text*, centered in the app's main window + + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + @discardableResult + open class func showBlockingWaitOverlayWithText(_ text: String) -> UIView { + let blocker = addMainWindowBlocker() + showCenteredWaitOverlayWithText(blocker, text: text) + + return blocker + } + + /** + Shows *blocking* overlay *with image and text*,, centered in the app's main window + + - parameter image: Image to be added to overlay + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + open class func showBlockingImageAndTextOverlay(_ image: UIImage, text: String) -> UIView { + let blocker = addMainWindowBlocker() + showImageAndTextOverlay(blocker, image: image, text: text) + + return blocker + } + + /** + Shows *text-only* overlay, centered in the app's main window + + - parameter text: Text to be shown on overlay + + - returns: Created overlay + */ + open class func showBlockingTextOverlay(_ text: String) -> UIView { + let blocker = addMainWindowBlocker() + showTextOverlay(blocker, text: text) + + return blocker + } + + /** + Removes all *blocking* overlays from application's main window + */ + open class func removeAllBlockingOverlays() { + let window = UIApplication.shared.delegate!.window!! + removeAllOverlaysFromView(window) + } + + // MARK: Non-blocking + @discardableResult + open class func showCenteredWaitOverlay(_ parentView: UIView) -> UIView { + let ai = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.whiteLarge) + ai.startAnimating() + + let containerViewRect = CGRect(x: 0, + y: 0, + width: ai.frame.size.width * 2, + height: ai.frame.size.height * 2) + + let containerView = UIView(frame: containerViewRect) + + containerView.tag = containerViewTag + containerView.layer.cornerRadius = cornerRadius + containerView.backgroundColor = backgroundColor + containerView.center = CGPoint(x: parentView.bounds.size.width/2, + y: parentView.bounds.size.height/2); + + ai.center = CGPoint(x: containerView.bounds.size.width/2, + y: containerView.bounds.size.height/2); + + containerView.addSubview(ai) + + parentView.addSubview(containerView) + + Utils.centerViewInSuperview(containerView) + + return containerView + } + + @discardableResult + open class func showCenteredWaitOverlayWithText(_ parentView: UIView, text: String) -> UIView { + let ai = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.white) + ai.startAnimating() + + return showGenericOverlay(parentView, text: text, accessoryView: ai) + } + + @discardableResult + open class func showImageAndTextOverlay(_ parentView: UIView, image: UIImage, text: String) -> UIView { + let imageView = UIImageView(image: image) + + return showGenericOverlay(parentView, text: text, accessoryView: imageView) + } + + open class func showGenericOverlay(_ parentView: UIView, text: String, accessoryView: UIView, horizontalLayout: Bool = true) -> UIView { + let label = labelForText(text) + var actualSize = CGSize.zero + + if horizontalLayout { + actualSize = CGSize(width: accessoryView.frame.size.width + label.frame.size.width + padding * 3, + height: max(label.frame.size.height, accessoryView.frame.size.height) + padding * 2) + + label.frame = label.frame.offsetBy(dx: accessoryView.frame.size.width + padding * 2, dy: padding) + + accessoryView.frame = accessoryView.frame.offsetBy(dx: padding, dy: (actualSize.height - accessoryView.frame.size.height)/2) + } else { + actualSize = CGSize(width: max(accessoryView.frame.size.width, label.frame.size.width) + padding * 2, + height: label.frame.size.height + accessoryView.frame.size.height + padding * 3) + + label.frame = label.frame.offsetBy(dx: padding, dy: accessoryView.frame.size.height + padding * 2) + + accessoryView.frame = accessoryView.frame.offsetBy(dx: (actualSize.width - accessoryView.frame.size.width)/2, dy: padding) + } + + // Container view + let containerViewRect = CGRect(x: 0, + y: 0, + width: actualSize.width, + height: actualSize.height) + + let containerView = UIView(frame: containerViewRect) + + containerView.tag = containerViewTag + containerView.layer.cornerRadius = cornerRadius + containerView.backgroundColor = backgroundColor + containerView.center = CGPoint(x: parentView.bounds.size.width/2, + y: parentView.bounds.size.height/2) + + containerView.addSubview(accessoryView) + containerView.addSubview(label) + + parentView.addSubview(containerView) + + Utils.centerViewInSuperview(containerView) + + return containerView + } + + @discardableResult + open class func showTextOverlay(_ parentView: UIView, text: String) -> UIView { + let label = labelForText(text) + label.frame = label.frame.offsetBy(dx: padding, dy: padding) + + let actualSize = CGSize(width: label.frame.size.width + padding * 2, + height: label.frame.size.height + padding * 2) + + // Container view + let containerViewRect = CGRect(x: 0, + y: 0, + width: actualSize.width, + height: actualSize.height) + + let containerView = UIView(frame: containerViewRect) + + containerView.tag = containerViewTag + containerView.layer.cornerRadius = cornerRadius + containerView.backgroundColor = backgroundColor + containerView.center = CGPoint(x: parentView.bounds.size.width/2, + y: parentView.bounds.size.height/2); + + containerView.addSubview(label) + + parentView.addSubview(containerView) + + Utils.centerViewInSuperview(containerView) + + + return containerView + } + + open class func showProgressOverlay(_ parentView: UIView, text: String) -> UIView { + let pv = UIProgressView(progressViewStyle: .default) + + return showGenericOverlay(parentView, text: text, accessoryView: pv, horizontalLayout: false) + } + + open class func removeAllOverlaysFromView(_ parentView: UIView) { + var overlay: UIView? + + while true { + overlay = parentView.viewWithTag(containerViewTag) + if overlay == nil { + break + } + + overlay!.removeFromSuperview() + } + } + + open class func updateOverlayText(_ parentView: UIView, text: String) { + if let overlay = parentView.viewWithTag(containerViewTag) { + for subview in overlay.subviews { + if let label = subview as? UILabel { + label.text = text as String + break + } + } + } + } + + open class func updateOverlayProgress(_ parentView: UIView, progress: Float) { + if let overlay = parentView.viewWithTag(containerViewTag) { + for subview in overlay.subviews { + if let pv = subview as? UIProgressView { + pv.progress = progress + break + } + } + } + } + + // MARK: Status bar notification + + open class func showAnnoyingNotificationOnTopOfStatusBar(_ notificationView: UIView, duration: TimeInterval, animated: Bool = true) { + if bannerWindow == nil { + bannerWindow = UIWindow() + bannerWindow!.windowLevel = UIWindowLevelStatusBar + 1 + bannerWindow!.backgroundColor = UIColor.clear + } + + bannerWindow!.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: notificationView.frame.size.height) + bannerWindow!.isHidden = false + + let selector = #selector(closeAnnoyingNotificationOnTopOfStatusBar) + let gestureRecognizer = UITapGestureRecognizer(target: self, action: selector) + notificationView.addGestureRecognizer(gestureRecognizer) + + bannerWindow!.addSubview(notificationView) + + if animated { + let frame = notificationView.frame + let origin = CGPoint(x: 0, y: -frame.height) + notificationView.frame = CGRect(origin: origin, size: frame.size) + + // Show appearing animation, schedule calling closing selector after completed + UIView.animate(withDuration: bannerDissapearAnimationDuration, animations: { + let frame = notificationView.frame + notificationView.frame = frame.offsetBy(dx: 0, dy: frame.height) + }, completion: { (finished) in + self.perform(selector, with: notificationView, afterDelay: duration) + }) + } else { + // Schedule calling closing selector right away + self.perform(selector, with: notificationView, afterDelay: duration) + } + } + + open class func closeAnnoyingNotificationOnTopOfStatusBar(_ sender: AnyObject) { + NSObject.cancelPreviousPerformRequests(withTarget: self) + + var notificationView: UIView? + + if sender.isKind(of: UITapGestureRecognizer.self) { + notificationView = (sender as! UITapGestureRecognizer).view! + } else if sender.isKind(of: UIView.self) { + notificationView = (sender as! UIView) + } + + UIView.animate(withDuration: bannerDissapearAnimationDuration, + animations: { () -> Void in + if let frame = notificationView?.frame { + notificationView?.frame = frame.offsetBy(dx: 0, dy: -frame.size.height) + } + }, + completion: { (finished) -> Void in + notificationView?.removeFromSuperview() + + bannerWindow?.isHidden = true + } + ) + } + + // MARK: - Private class methods - + + fileprivate class func labelForText(_ text: String) -> UILabel { + let textSize = text.size(attributes: [NSFontAttributeName: font]) + + let labelRect = CGRect(x: 0, + y: 0, + width: textSize.width, + height: textSize.height) + + let label = UILabel(frame: labelRect) + label.font = font + label.textColor = textColor + label.text = text as String + label.numberOfLines = 0 + + return label; + } + + fileprivate class func addMainWindowBlocker() -> UIView { + let window = UIApplication.shared.delegate!.window!! + + let blocker = UIView(frame: window.bounds) + blocker.backgroundColor = backgroundColor + blocker.tag = containerViewTag + + blocker.translatesAutoresizingMaskIntoConstraints = false + + window.addSubview(blocker) + + let viewsDictionary = ["blocker": blocker] + + // Add constraints to handle orientation change + let constraintsV = NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[blocker]-0-|", + options: NSLayoutFormatOptions(rawValue: 0), + metrics: nil, + views: viewsDictionary) + + let constraintsH = NSLayoutConstraint.constraints(withVisualFormat: "|-0-[blocker]-0-|", + options: NSLayoutFormatOptions(rawValue: 0), + metrics: nil, + views: viewsDictionary) + + window.addConstraints(constraintsV + constraintsH) + + return blocker + } +} diff --git a/Pods/SwiftyAttributes/LICENSE b/Pods/SwiftyAttributes/LICENSE new file mode 100644 index 0000000..44f149e --- /dev/null +++ b/Pods/SwiftyAttributes/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Eddie Kaiger + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Pods/SwiftyAttributes/README.md b/Pods/SwiftyAttributes/README.md new file mode 100644 index 0000000..3dea341 --- /dev/null +++ b/Pods/SwiftyAttributes/README.md @@ -0,0 +1,151 @@ +# SwiftyAttributes + +#### *Swift extensions that make it a breeze to work with attributed strings.* + +![Swift Version](https://img.shields.io/badge/swift-3.0-orange.svg?style=flat) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/SwiftyAttributes.svg)](https://img.shields.io/cocoapods/v/SwiftyAttributes.svg) +[![Platform](https://img.shields.io/cocoapods/p/SwiftyAttributes.svg?style=flat)](http://cocoapods.org/pods/SwiftyAttributes) +[![Travis CI](https://travis-ci.org/eddiekaiger/SwiftyAttributes.svg?branch=master)](https://travis-ci.org/eddiekaiger/SwiftyAttributes.svg?branch=master) +[![codecov.io](http://codecov.io/github/eddiekaiger/SwiftyAttributes/coverage.svg?branch=master)](http://codecov.io/github/eddiekaiger/SwiftyAttributes/coverage.svg?branch=master) + +--- + +The **original** way to create an attributed string in Swift: + +````swift +let attributes: [String: Any] = [ + NSForegroundColorAttributeName: UIColor.blue, + NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleSingle.rawValue) +] +let fancyString = NSAttributedString(string: "Hello World!", attributes: attributes) +```` + +With **SwiftyAttributes**, you can write the same thing like this: + +````swift +let fancyString = "Hello World!".withTextColor(.blue).withUnderlineStyle(.styleSingle) +```` + +Alternatively, **SwiftyAttributes** provides an `Attribute` enum: +````swift +let fancyString = "Hello World!".withAttributes([ + .backgroundColor(.magenta), + .strokeColor(.orange), + .strokeWidth(1), + .baselineOffset(5.2) +]) +```` + +You can also easily combine attributed strings using a plus sign: + +````swift +let fancyString = "Hello".withFont(.systemFont(ofSize: 12)) + " World!".withFont(.systemFont(ofSize: 18)) +```` + +**SwiftyAttributes** has support for *every* attribute that can be used in iOS. + +# Requirements + +* iOS 8.0+ + +# Installation + +### With CocoaPods + +#### For **Swift 3**: + +`pod 'SwiftyAttributes'` + +> For **Swift 2.3**: + +> `pod 'SwiftyAttributes', '1.1'` + +> If using Xcode 8, you may need to add this to end of your Podfile: + +> ```swift +> post_install do |installer| +> installer.pods_project.targets.each do |target| +> target.build_configurations.each do |config| +> config.build_settings["SWIFT_VERSION"] = "2.3" +> end +> end +> end +> ``` + +### With Carthage + +#### For **Swift 3**: + +`github "eddiekaiger/SwiftyAttributes"` + +> For **Swift 2.3**: + +> `github "eddiekaiger/SwiftyAttributes" == 1.1.1` + +# Usage + +Initializing attributed strings in `SwiftyAttributes` can be done several ways: + +- Using the `with[Attribute]` extensions: + ````swift + "Hello World".withUnderlineColor(.red).withUnderlineStyle(.styleDouble) + ```` + +- Using the `Attribute` enum extensions: + ````swift + "Hello World".withAttributes([.underlineColor(.red), .underlineStyle(.styleDouble)]) + ```` + +- Using the `Attribute` enum in an initializer: + ````swift + NSAttributedString(string: "Hello World", attributes: [.kern(5), .backgroundColor(.gray)]) + ```` + +You can retrieve the attribute at a specific location using an attribute name from the `Attribute.Name` enum: +````swift +let attr: Attribute? = myAttributedString.attribute(.shadow, at: 5) +```` + +Several API methods are provided to use these new enums as well as Swift's `Range` type instead of `NSRange`. Some of the method signatures include: + +````swift +extension NSMutableAttributedString { + func addAttributes(_ attributes: [Attribute], range: Range) + func addAttributes(_ attributes: [Attribute], range: NSRange) + func setAttributes(_ attributes: [Attribute], range: Range) + func setAttributes(_ attributes: [Attribute], range: NSRange) + func replaceCharacters(in range: Range, with str: String) + func replaceCharacters(in range: Range, with attrString: NSAttributedString) + func deleteCharacters(in range: Range) + func removeAttribute(_ name: Attribute.Name, range: Range) +} + +extension NSAttributedString { + convenience init(string str: String, attributes: [Attribute]) + func withAttributes(_ attributes: [Attribute]) -> NSMutableAttributedString + func withAttribute(_ attribute: Attribute) -> NSMutableAttributedString + func attributedSubstring(from range: Range) -> NSAttributedString + func attribute(_ attrName: Attribute.Name, at location: Int, effectiveRange range: NSRangePointer? = nil) -> Attribute? + func attributes(in range: Range, options: NSAttributedString.EnumerationOptions = []) -> [([Attribute], Range)] + func enumerateAttributes(in enumerationRange: Range, options: NSAttributedString.EnumerationOptions = [], using block: (_ attrs: [Attribute], _ range: Range, _ stop: UnsafeMutablePointer) -> Void) + func enumerateAttribute(_ attrName: Attribute.Name, in enumerationRange: Range, options: NSAttributedString.EnumerationOptions = [], using block: (_ value: Any?, _ range: Range, _ stop: UnsafeMutablePointer) -> Void) +} + +extension String { + var attributedString: NSMutableAttributedString + func withAttributes(_ attributes: [Attribute]) -> NSMutableAttributedString + func withAttribute(_ attribute: Attribute) -> NSMutableAttributedString +} + +// ... and more! + +```` + +# Support + +For questions, support, and suggestions, please open up an issue. + +# License + +**SwiftyAttributes** is available under the MIT license. See the LICENSE file for more info. diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute+Sequence.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute+Sequence.swift new file mode 100644 index 0000000..c8d12ba --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute+Sequence.swift @@ -0,0 +1,41 @@ +// +// AttributeConversions.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 11/23/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +/** + An extension on dictionaries that allows us to convert a Foundation-based dictionary of attributes to an array of `Attribute`s. + + A Sequence with an iterator of (String, Any) is equivalent to [String: Any] + This is a simple syntactic workaround since we can't write "extension Dictionary where Key == String". Thanks Swift :) + */ +extension Sequence where Iterator.Element == (key: String, value: Any) { + + /// Returns an array of `Attribute`s converted from the dictionary of attributes. Use this whenever you want to convert [String: Any] to [Attribute]. + public var swiftyAttributes: [Attribute] { + return flatMap { name, value in + if let attrName = Attribute.Name(rawValue: name) { + return Attribute(name: attrName, foundationValue: value) + } else { + return nil + } + } + } + +} + +extension Sequence where Iterator.Element == Attribute { + + /// Returns the attribute dictionary required by Foundation's API for attributed strings. Use this whenever you need to convert [Attribute] to [String: Any]. + public var foundationAttributes: [String: Any] { + return reduce([String: Any]()) { dictionary, attribute in + var dict = dictionary + dict[attribute.keyName] = attribute.foundationValue + return dict + } + } + +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute.swift new file mode 100644 index 0000000..3149dd1 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Attribute.swift @@ -0,0 +1,330 @@ +// +// Attribute.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/15/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +#if os(macOS) + import AppKit + public typealias Color = NSColor + public typealias Font = NSFont + public typealias Cursor = NSCursor + public typealias TextAlternatives = NSTextAlternatives +#else + import UIKit + public typealias Color = UIColor + public typealias Font = UIFont +#endif + +public typealias UnderlineStyle = NSUnderlineStyle +public typealias StrikethroughStyle = NSUnderlineStyle +public typealias ParagraphStyle = NSParagraphStyle + +#if os(watchOS) +#else +public typealias Shadow = NSShadow +public typealias TextAttachment = NSTextAttachment +#endif + +/** + Represents attributes that can be applied to NSAttributedStrings. + */ +public enum Attribute { + + #if os(watchOS) + #else + /// Attachment attribute that allows items like images to be inserted into text. + case attachment(TextAttachment) + #endif + + /// Value indicating the character's offset from the baseline, in points. + case baselineOffset(Double) + + /// The background color of the attributed string. + case backgroundColor(Color) + + #if os(macOS) + /// The cursor object associated with the attributed string. + case cursor(Cursor) + #endif + + /// Value indicating the log of the expansion factor to be applied to glyphs. + case expansion(Double) + + /// The font of the attributed string. + case font(Font) + + /// Specifies the number of points by which to adjust kern-pair characters. Kerning prevents unwanted space from occurring between specific characters and depends on the font. The value 0 means kerning is disabled (default). + case kern(Double) + + /// Ligatures cause specific character combinations to be rendered using a single custom glyph that corresponds to those characters. See `Ligatures` for values. + case ligatures(Ligatures) + + /// A URL link to attach to the attributed string. + case link(URL) + + #if os(macOS) + /// The index in marked text indicating clause segments. + case markedClauseSegment(Int) + #endif + + /// A value indicating the skew to be applied to glyphs. + case obliqueness(Double) + + /// An `NSParagraphStyle` to be applied to the attributed string. + case paragraphStyle(ParagraphStyle) + + #if os(watchOS) + #else + /// A shadow to be applied to the characters. + case shadow(Shadow) + #endif + + #if os(macOS) + /// A state indicating a spelling or grammar error. See `SpellingState` for possible values. + case spellingState(SpellingState) + #endif + + /// The color of the stroke (border) around the characters. + case strokeColor(Color) + + /// The width/thickness of the stroke (border) around the characters. + case strokeWidth(Double) + + /// The color of the strikethrough. + case strikethroughColor(Color) + + /// The style of the strikethrough. + case strikethroughStyle(StrikethroughStyle) + + #if os(macOS) + /// The superscript attribute. + case superscript(Int) + + /// The object representing alternatives for a string that may be presented to the user. + case textAlternatives(TextAlternatives) + #endif + + /// The text color. + case textColor(Color) + + /// The text effect to apply. See `TextEffect` for possible values. + case textEffect(TextEffect) + + #if os(macOS) + /// The text of the tooltip. + case toolTip(String) + #endif + + /// The color of the underline. + case underlineColor(Color) + + /// The style of the underline. + case underlineStyle(UnderlineStyle) + + /// The vertical glyph form (horizontal or vertical text). See `VerticalGlyphForm` for details. + case verticalGlyphForm(VerticalGlyphForm) + + /// The writing directions to apply to the attributed string. See `WritingDirection` for values. Only available on iOS 9.0+. + case writingDirections([WritingDirection]) + + init(name: Attribute.Name, foundationValue: Any) { + func validate(_ val: Any) -> Type { + assert(val is Type, "Attribute with name \(name.rawValue) must have a value of type \(Type.self)") + return val as! Type + } + + func validateDouble(_ val: Any) -> Double { + assert(val is NSNumber, "Attribute with name \(name.rawValue) must have a value that is castable to NSNumber") + return (val as! NSNumber).doubleValue + } + + var ret: Attribute! + + // Bug in Swift prevents us from putting directives inside switch statements (https://bugs.swift.org/browse/SR-2) + + #if os(watchOS) + #else + switch name { + case .attachment: ret = .attachment(validate(foundationValue)) + case .shadow: ret = .shadow(validate(foundationValue)) + default: break + } + #endif + + #if os(macOS) + switch name { + case .cursor: ret = .cursor(validate(foundationValue)) + case .markedClauseSegment: ret = .markedClauseSegment(validate(foundationValue)) + case .spellingState: ret = .spellingState(SpellingState(rawValue: validate(foundationValue))!) + case .superscript: ret = .superscript(validate(foundationValue)) + case .textAlternatives: ret = .textAlternatives(validate(foundationValue)) + case .toolTip: ret = .toolTip(validate(foundationValue)) + default: break + } + #endif + + switch name { + case .baselineOffset: ret = .baselineOffset(validateDouble(foundationValue)) + case .backgroundColor: ret = .backgroundColor(validate(foundationValue)) + case .expansion: ret = .expansion(validateDouble(foundationValue)) + case .font: ret = .font(validate(foundationValue)) + case .kern: ret = .kern(validateDouble(foundationValue)) + case .ligature: ret = .ligatures(Ligatures(rawValue: validate(foundationValue))!) + case .link: ret = .link(validate(foundationValue)) + case .obliqueness: ret = .obliqueness(validateDouble(foundationValue)) + case .paragraphStyle: ret = .paragraphStyle(validate(foundationValue)) + case .strokeColor: ret = .strokeColor(validate(foundationValue)) + case .strokeWidth: ret = .strokeWidth(validateDouble(foundationValue)) + case .strikethroughColor: ret = .strikethroughColor(validate(foundationValue)) + case .strikethroughStyle: ret = .strikethroughStyle(StrikethroughStyle(rawValue: validate(foundationValue))!) + case .textColor: ret = .textColor(validate(foundationValue)) + case .textEffect: ret = .textEffect(TextEffect(rawValue: validate(foundationValue))!) + case .underlineColor: ret = .underlineColor(validate(foundationValue)) + case .underlineStyle: ret = .underlineStyle(UnderlineStyle(rawValue: validate(foundationValue))!) + case .verticalGlyphForm: ret = .verticalGlyphForm(VerticalGlyphForm(rawValue: validate(foundationValue))!) + case .writingDirection: + let values: [Int] = validate(foundationValue) + ret = .writingDirections(values.flatMap(WritingDirection.init)) + default: break + } + + self = ret + } + + /// The key name corresponding to the attribute. + public var keyName: String { + + var name: Attribute.Name! + + // Bug in Swift prevents us from putting directives inside switch statements (https://bugs.swift.org/browse/SR-2) + + #if os(watchOS) + #else + switch self { + case .attachment(_): name = .attachment + case .shadow(_): name = .shadow + default: break + } + #endif + + #if os(macOS) + switch self { + case .cursor(_): name = .cursor + case .markedClauseSegment(_): name = .markedClauseSegment + case .spellingState(_): name = .spellingState + case .superscript(_): name = .superscript + case .textAlternatives(_): name = .textAlternatives + case .toolTip(_): name = .toolTip + default: break + } + #endif + + switch self { + case .baselineOffset(_): name = .baselineOffset + case .backgroundColor(_): name = .backgroundColor + case .expansion(_): name = .expansion + case .font(_): name = .font + case .kern(_): name = .kern + case .ligatures(_): name = .ligature + case .link(_): name = .link + case .obliqueness(_): name = .obliqueness + case .paragraphStyle(_): name = .paragraphStyle + case .strokeColor(_): name = .strokeColor + case .strokeWidth(_): name = .strokeWidth + case .strikethroughColor(_): name = .strikethroughColor + case .strikethroughStyle(_): name = .strikethroughStyle + case .textColor(_): name = .textColor + case .textEffect(_): name = .textEffect + case .underlineColor(_): name = .underlineColor + case .underlineStyle(_): name = .underlineStyle + case .writingDirections(_): name = .writingDirection + case .verticalGlyphForm(_): name = .verticalGlyphForm + default: break + } + + return name.rawValue + } + + // Convenience getter variable for the associated value of the attribute. See each case to determine the return type. + public var value: Any { + + var ret: Any! + + // Bug in Swift prevents us from putting directives inside switch statements (https://bugs.swift.org/browse/SR-2) + + #if os(watchOS) + #else + switch self { + case .attachment(let attachment): ret = attachment + case .shadow(let shadow): ret = shadow + default: break + } + #endif + + #if os(macOS) + switch self { + case .cursor(let cursor): ret = cursor + case .markedClauseSegment(let segment): ret = segment + case .spellingState(let state): ret = state + case .superscript(let superscript): ret = superscript + case .textAlternatives(let alternatives): ret = alternatives + case .toolTip(let text): ret = text + default: break + } + #endif + + switch self { + case .baselineOffset(let offset): ret = offset + case .backgroundColor(let color): ret = color + case .expansion(let expansion): ret = expansion + case .font(let font): ret = font + case .kern(let kern): ret = kern + case .ligatures(let ligatures): ret = ligatures + case .link(let link): ret = link + case .obliqueness(let value): ret = value + case .paragraphStyle(let style): ret = style + case .strokeColor(let color): ret = color + case .strokeWidth(let width): ret = width + case .strikethroughColor(let color): ret = color + case .strikethroughStyle(let style): ret = style + case .textColor(let color): ret = color + case .textEffect(let effect): ret = effect + case .underlineColor(let color): ret = color + case .underlineStyle(let style): ret = style + case .verticalGlyphForm(let form): ret = form + case .writingDirections(let directions): ret = directions + default: break + } + + return ret + } + + /// The value that is passed into the original attribute dictionary of Foundation's API for NSAttributedStrings. Consists of basic types such as Int, Color, Font, etc. + public var foundationValue: Any { + #if os(macOS) + switch self { + case .spellingState(let state): return state.rawValue + default: break + } + #endif + + switch self { + case .ligatures(let ligatures): return ligatures.rawValue + case .strikethroughStyle(let style): return style.rawValue + case .textEffect(let effect): return effect.rawValue + case .underlineStyle(let style): return style.rawValue + case .writingDirections(let directions): return directions.map { $0.rawValue } + case .verticalGlyphForm(let form): return form.rawValue + default: return value + } + } +} + +extension Attribute: Equatable { } + +public func == (lhs: Attribute, rhs: Attribute) -> Bool { + return (lhs.foundationValue as? NSObject) == (rhs.foundationValue as? NSObject) +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/AttributeName.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/AttributeName.swift new file mode 100644 index 0000000..37d8892 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/AttributeName.swift @@ -0,0 +1,181 @@ +// +// AttributeName.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 11/23/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +#if os(macOS) + import AppKit +#else + import UIKit +#endif + +extension Attribute { + + /** + An enum that corresponds to `Attribute`, mapping attributes to their respective names. + */ + public enum Name: RawRepresentable { + #if os(watchOS) + #else + case attachment + #endif + case baselineOffset + case backgroundColor + #if os(macOS) + case cursor + #endif + case expansion + case font + case kern + case ligature + case link + #if os(macOS) + case markedClauseSegment + #endif + case obliqueness + case paragraphStyle + #if os(watchOS) + #else + case shadow + #endif + #if os(macOS) + case spellingState + #endif + case strokeColor + case strokeWidth + case strikethroughColor + case strikethroughStyle + #if os(macOS) + case superscript + case textAlternatives + #endif + case textColor + case textEffect + #if os(macOS) + case toolTip + #endif + case underlineColor + case underlineStyle + case verticalGlyphForm + case writingDirection + + public init?(rawValue: String) { + + #if os(macOS) + switch rawValue { + case NSCursorAttributeName: + self = .cursor + return + case NSMarkedClauseSegmentAttributeName: + self = .markedClauseSegment + return + case NSSpellingStateAttributeName: + self = .spellingState + return + case NSSuperscriptAttributeName: + self = .superscript + return + case NSTextAlternativesAttributeName: + self = .textAlternatives + return + case NSToolTipAttributeName: + self = .toolTip + return + default: break + } + #endif + + switch rawValue { + case NSAttachmentAttributeName: + #if os(watchOS) + return nil + #else + self = .attachment + #endif + case NSBaselineOffsetAttributeName: self = .baselineOffset + case NSBackgroundColorAttributeName: self = .backgroundColor + case NSExpansionAttributeName: self = .expansion + case NSFontAttributeName: self = .font + case NSKernAttributeName: self = .kern + case NSLigatureAttributeName: self = .ligature + case NSLinkAttributeName: self = .link + case NSObliquenessAttributeName: self = .obliqueness + case NSParagraphStyleAttributeName: self = .paragraphStyle + case NSShadowAttributeName: + #if os(watchOS) + return nil + #else + self = .shadow + #endif + case NSStrokeColorAttributeName: self = .strokeColor + case NSStrokeWidthAttributeName: self = .strokeWidth + case NSStrikethroughColorAttributeName: self = .strikethroughColor + case NSStrikethroughStyleAttributeName: self = .strikethroughStyle + case NSForegroundColorAttributeName: self = .textColor + case NSTextEffectAttributeName: self = .textEffect + case NSUnderlineColorAttributeName: self = .underlineColor + case NSUnderlineStyleAttributeName: self = .underlineStyle + case NSVerticalGlyphFormAttributeName: self = .verticalGlyphForm + case NSWritingDirectionAttributeName: self = .writingDirection + default: return nil + } + } + + public var rawValue: String { + + var name: String! + + // Bug in Swift prevents us from putting directives inside switch statements (https://bugs.swift.org/browse/SR-2) + + #if os(watchOS) + #else + switch self { + case .attachment: name = NSAttachmentAttributeName + case .shadow: name = NSShadowAttributeName + default: break + } + #endif + + #if os(macOS) + switch self { + case .cursor: name = NSCursorAttributeName + case .markedClauseSegment: name = NSMarkedClauseSegmentAttributeName + case .spellingState: name = NSSpellingStateAttributeName + case .superscript: name = NSSuperscriptAttributeName + case .textAlternatives: name = NSTextAlternativesAttributeName + case .toolTip: name = NSToolTipAttributeName + default: break + } + #endif + + switch self { + case .baselineOffset: name = NSBaselineOffsetAttributeName + case .backgroundColor: name = NSBackgroundColorAttributeName + case .expansion: name = NSExpansionAttributeName + case .font: name = NSFontAttributeName + case .kern: name = NSKernAttributeName + case .ligature: name = NSLigatureAttributeName + case .link: name = NSLinkAttributeName + case .obliqueness: name = NSObliquenessAttributeName + case .paragraphStyle: name = NSParagraphStyleAttributeName + case .strokeColor: name = NSStrokeColorAttributeName + case .strokeWidth: name = NSStrokeWidthAttributeName + case .strikethroughColor: name = NSStrikethroughColorAttributeName + case .strikethroughStyle: name = NSStrikethroughStyleAttributeName + case .textColor: name = NSForegroundColorAttributeName + case .textEffect: name = NSTextEffectAttributeName + case .underlineColor: name = NSUnderlineColorAttributeName + case .underlineStyle: name = NSUnderlineStyleAttributeName + case .verticalGlyphForm: name = NSVerticalGlyphFormAttributeName + case .writingDirection: name = NSWritingDirectionAttributeName + default: break + } + + return name + } + } + +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Ligatures.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Ligatures.swift new file mode 100644 index 0000000..a8ccfe9 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Ligatures.swift @@ -0,0 +1,21 @@ +// +// Ligatures.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/5/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +import Foundation + +/** + Ligatures cause specific character combinations to be rendered using a single custom glyph that corresponds to those characters. + */ +public enum Ligatures: Int { + + /// Correspond to no ligatures. + case none + + /// Corresponds to the use of the default ligatures. + case `default` +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSAttributedString+SwiftyAttributes.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSAttributedString+SwiftyAttributes.swift new file mode 100644 index 0000000..2116d0e --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSAttributedString+SwiftyAttributes.swift @@ -0,0 +1,361 @@ +// +// NSAttributedString+SwiftyAttributes.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/25/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +import Foundation + +func dictionary(from attributes: [Attribute]) -> [String: Any] { + var dict = [String: Any]() + for attr in attributes { + dict[attr.keyName] = attr.foundationValue + } + return dict +} + +extension NSAttributedString { + + /** + Creates a new `NSAttributedString` with the specified attributes. + + - parameter str: The string for the new attributed string. + - parameter attributes: The attributes for the new attributed string. + */ + public convenience init(string str: String, attributes: [Attribute]) { + self.init(string: str, attributes: dictionary(from: attributes)) + } + + /** + Returns an attributed string with the specified attributes added. + + - parameter attributes: The attributes to add to the new attributed string. + - returns: An `NSMutableAttributedString` with the new attributes applied. + */ + public func withAttributes(_ attributes: [Attribute]) -> NSMutableAttributedString { + let mutable = mutableCopy() as! NSMutableAttributedString + mutable.addAttributes(dictionary(from: attributes), range: NSRange(location: 0, length: length)) + return mutable + } + + /** + Returns an attributed string with the specified attribute added. + + - parameter attribute: The attribute to add to the new attributed string. + - returns: An `NSMutableAttributedString` with the new attribute applied. + */ + public func withAttribute(_ attribute: Attribute) -> NSMutableAttributedString { + return withAttributes([attribute]) + } + + /** + Returns an `NSAttributedString` object consisting of the characters and attributes within a given range in the receiver. + + - parameter range: The range from which to create a new attributed string. `range` must lie within the bounds of the receiver. + - returns: An `NSAttributedString` object consisting of the characters and attributes within `range` in the receiver. + */ + public func attributedSubstring(from range: Range) -> NSAttributedString { + return attributedSubstring(from: NSRange(range)) + } + + /** + Returns the value for an attribute with a given name of the character at a given index, and by reference the range over which the attribute applies. + + - parameters: + - attrName: The name of an attribute. + - location: The index for which to return attributes. This value must not exceed the bounds of the receiver. + - range: + If non-nil: + - If the named attribute exists at `location`, upon return `range` contains a range over which the named attribute’s value applies. + - If the named attribute does not exist at `location`, upon return `range` contains the range over which the attribute does not exist. + + The range isn’t necessarily the maximum range covered by `attrName`, and its extent is implementation-dependent. + If you need the maximum range, use attribute(_:at:longestEffectiveRange:in:). If you don't need this value, pass `nil`. + */ + public func attribute(_ attrName: Attribute.Name, at location: Int, effectiveRange range: NSRangePointer? = nil) -> Attribute? { + if let attributeValue = attribute(attrName.rawValue, at: location, effectiveRange: range) { + return Attribute(name: attrName, foundationValue: attributeValue) + } + return nil + } + + /** + Returns the enumerated attributes in a specified range as an array of attribute-range pairs. + + - parameters: + - range: Contains the maximum range over which the attributes are enumerated. + - options: The options used by the enumeration. The values can be combined using C-bitwise OR. The values are described in `NSAttributedString.EnumerationOptions`. + - returns: An array of attribute-range tuples. Each tuples contains a range and the array of attributes that exist in that range. + */ + public func attributes(in range: Range, options: NSAttributedString.EnumerationOptions = []) -> [([Attribute], Range)] { + var attributeRanges = [([Attribute], Range)]() + enumerateAttributes(in: range, options: options) { attributes, range, _ in + attributeRanges.append((attributes, range)) + } + return attributeRanges + } + + /** + Executes the block for each attribute in the range. For discussion, see documentation for `NSAttributedString.enumerateAttributes(in:options:using:)`. + + - parameters: + + - enumerationRange: Contains the maximum range over which the attributes and values are enumerated, clipped to enumerationRange. + - options: The options used by the enumeration. The values can be combined using C-bitwise OR. The values are described in `NSAttributedString.EnumerationOptions`. + - block: The block to apply to ranges of the attribute in the attributed string. The block takes three arguments: + + + attrs: The attributes for the range. + + range: The run of the attributes. + + stop: A reference to a Boolean value. The block can set the value to `true` to stop further processing of the set. + The stop argument is an out-only argument. You should only ever set this Boolean to `true` within the block. + */ + public func enumerateAttributes(in enumerationRange: Range, options: NSAttributedString.EnumerationOptions = [], using block: (_ attrs: [Attribute], _ range: Range, _ stop: UnsafeMutablePointer) -> Void) { + enumerateAttributes(in: NSRange(enumerationRange), options: options) { attributes, range, ptr in + block(attributes.swiftyAttributes, range.location ..< (range.location + range.length), ptr) + } + } + + /** + Executes the block for the specified attribute run in the specified range. For discussion, see documentation for `NSAttributedString.enumerateAttribute(_:in:options:using:)`. + + - parameters: + + - attrName: The name of an attribute. + - enumerationRange: Contains the maximum range over which the attributes and values are enumerated, clipped to enumerationRange. + - options: The options used by the enumeration. The values can be combined using C-bitwise OR. The values are described in `NSAttributedString.EnumerationOptions`. + - block: The block to apply to ranges of the attribute in the attributed string. The block takes three arguments: + + + value: The value of the attribute. + + range: A range containing the run of the attribute. + + stop: A reference to a Boolean value. The block can set the value to `true` to stop further processing of the set. + The stop argument is an out-only argument. You should only ever set this Boolean to `true` within the block. + */ + public func enumerateAttribute(_ attrName: Attribute.Name, in enumerationRange: Range, options: NSAttributedString.EnumerationOptions = [], using block: (_ value: Any?, _ range: Range, _ stop: UnsafeMutablePointer) -> Void) { + enumerateAttribute(attrName.rawValue, in: NSRange(enumerationRange), options: options) { value, range, ptr in + block(value, range.location ..< (range.location + range.length), ptr) + } + } + +} + +extension NSAttributedString { + + /** + Creates an attributed string with a specific font. + + - parameter font: The font to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withFont(_ font: Font) -> NSMutableAttributedString { + return withAttribute(.font(font)) + } + + /** + Creates an attributed string with a specific paragraph style. + + - parameter style: The font to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withParagraphStyle(_ style: ParagraphStyle) -> NSMutableAttributedString { + return withAttribute(.paragraphStyle(style)) + } + + /** + Creates an attributed string with a specific text color. + + - parameter color: The text color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withTextColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.textColor(color)) + } + + /** + Creates an attributed string with a specific background color. + + - parameter color: The background color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withBackgroundColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.backgroundColor(color)) + } + + /** + Creates an attributed string with a specific ligature. + + - parameter ligatures: The font to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withLigatures(_ ligatures: Ligatures) -> NSMutableAttributedString { + return withAttribute(.ligatures(ligatures)) + } + + /** + Creates an attributed string with a specific kern. + + - parameter kernValue: The kern value to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withKern(_ kernValue: Double) -> NSMutableAttributedString { + return withAttribute(.kern(kernValue)) + } + + /** + Creates an attributed string with a specific strikethrough style. + + - parameter style: The strikethrough style to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrikethroughStyle(_ style: UnderlineStyle) -> NSMutableAttributedString { + return withAttribute(.strikethroughStyle(style)) + } + + /** + Creates an attributed string with a specific underline style. + + - parameter style: The underline style to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withUnderlineStyle(_ style: UnderlineStyle) -> NSMutableAttributedString { + return withAttribute(.underlineStyle(style)) + } + + /** + Creates an attributed string with a specific stroke color. + + - parameter color: The stroke color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrokeColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.strokeColor(color)) + } + + /** + Creates an attributed string with a specific stroke width. + + - parameter width: The stroke width to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrokeWidth(_ width: Double) -> NSMutableAttributedString { + return withAttribute(.strokeWidth(width)) + } + + #if os(watchOS) + #else + /** + Creates an attributed string with a specific shadow. + + - parameter shadow: The shadow to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withShadow(_ shadow: Shadow) -> NSMutableAttributedString { + return withAttribute(.shadow(shadow)) + } + #endif + + /** + Creates an attributed string with a specific text effect. + + - parameter effect: The text effect to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withTextEffect(_ effect: TextEffect) -> NSMutableAttributedString { + return withAttribute(.textEffect(effect)) + } + + #if os(watchOS) + #else + /** + Creates an attributed string with a specific attachment. + + - parameter attachment: The attachment to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withAttachment(_ attachment: TextAttachment) -> NSMutableAttributedString { + return withAttribute(.attachment(attachment)) + } + #endif + + /** + Creates an attributed string with a specific link. + + - parameter link: The URL link to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withLink(_ link: URL) -> NSMutableAttributedString { + return withAttribute(.link(link)) + } + + /** + Creates an attributed string with a specific baseline offset. + + - parameter offset: The baseline offset to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withBaselineOffset(_ offset: Double) -> NSMutableAttributedString { + return withAttribute(.baselineOffset(offset)) + } + + /** + Creates an attributed string with a specific underline color. + + - parameter color: The underline color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withUnderlineColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.underlineColor(color)) + } + + /** + Creates an attributed string with a specific underline style. + + - parameter color: The underline style to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrikethroughColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.strikethroughColor(color)) + } + + /** + Creates an attributed string with a specific obliqueness. + + - parameter obliquenessValue: The obliqueness value to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withObliqueness(_ obliquenessValue: Double) -> NSMutableAttributedString { + return withAttribute(.obliqueness(obliquenessValue)) + } + + /** + Creates an attributed string with a specific expansion. + + - parameter expansion: The expansion value to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withExpansion(_ expansion: Double) -> NSMutableAttributedString { + return withAttribute(.expansion(expansion)) + } + + /** + Creates an attributed string with a specific vertical glyph form (horizontal or vertical writing direction). + + - parameter form: The horizontal/vertical writing direction to set for the attributed string. See `VerticalGlyphForm` for details. + - returns: A new attributed string with the newly added attribute. + */ + public func withVerticalGlyphForm(_ form: VerticalGlyphForm) -> NSMutableAttributedString { + return withAttribute(.verticalGlyphForm(form)) + } + + /** + Creates an attributed string with a specific writing direction. + + - parameter directions: The direction(s) to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withWritingDirections(_ directions: [WritingDirection]) -> NSMutableAttributedString { + return withAttribute(.writingDirections(directions)) + } + +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSMutableAttributedString+SwiftyAttributes.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSMutableAttributedString+SwiftyAttributes.swift new file mode 100644 index 0000000..6714af1 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/NSMutableAttributedString+SwiftyAttributes.swift @@ -0,0 +1,92 @@ +// +// NSMutableAttributedString+SwiftyAttributes.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/25/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +import Foundation + +extension NSMutableAttributedString { + + /** + Adds the given collection of attributes to the characters in the specified range. + + - parameter attributes: The attributes to add. + - parameter range: The range of characters to which the specified attributes apply. + */ + public func addAttributes(_ attributes: [Attribute], range: Range) { + addAttributes(attributes, range: NSRange(range)) + } + + /** + Adds the given collection of attributes to the characters in the specified range. + + - parameter attributes: The attributes to add. + - parameter range: The range of characters to which the specified attributes apply. + */ + public func addAttributes(_ attributes: [Attribute], range: NSRange) { + addAttributes(dictionary(from: attributes), range: range) + } + + /** + Sets the attributes for the characters in the specified range to the specified attributes. + + - parameter attributes: The attributes to set. + - parameter range: The range of characters whose attributes are set. + */ + public func setAttributes(_ attributes: [Attribute], range: Range) { + setAttributes(attributes, range: NSRange(range)) + } + + /** + Sets the attributes for the characters in the specified range to the specified attributes. + + - parameter attributes: The attributes to set. + - parameter range: The range of characters whose attributes are set. + */ + public func setAttributes(_ attributes: [Attribute], range: NSRange) { + setAttributes(dictionary(from: attributes), range: range) + } + + /** + Replaces the characters in the given range with the characters of the given string. + + - parameter range: A range specifying the characters to replace. + - parameter str: A string specifying the characters to replace those in `range`. + */ + public func replaceCharacters(in range: Range, with str: String) { + replaceCharacters(in: NSRange(range), with: str) + } + + /** + Replaces the characters and attributes in a given range with the characters and attributes of the given attributed string. + + - parameter range: The range of characters and attributes replaced. + - parameter attrString: The attributed string whose characters and attributes replace those in the specified range. + */ + public func replaceCharacters(in range: Range, with attrString: NSAttributedString) { + replaceCharacters(in: NSRange(range), with: attrString) + } + + /** + Deletes the characters in the given range along with their associated attributes. + + - parameter range: A range specifying the characters to delete. + */ + public func deleteCharacters(in range: Range) { + deleteCharacters(in: NSRange(range)) + } + + /** + Removes the named attribute from the characters in the specified range. + + - parameter name: The name of the attribute to remove. + - parameter range: The range of characters from which the specified attribute is removed. + */ + public func removeAttribute(_ name: Attribute.Name, range: Range) { + removeAttribute(name.rawValue, range: NSRange(range)) + } + +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Operators.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Operators.swift new file mode 100644 index 0000000..50a00e1 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/Operators.swift @@ -0,0 +1,18 @@ +// +// Operators.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/25/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +import Foundation + +/** + Overloaded addition operator for attributed strings. Creates a concatenated NSAttributedString. + */ +public func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSMutableAttributedString { + let combinedString = lhs.mutableCopy() as! NSMutableAttributedString + combinedString.append(rhs) + return combinedString +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/String+SwiftyAttributes.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/String+SwiftyAttributes.swift new file mode 100644 index 0000000..cf31837 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/String+SwiftyAttributes.swift @@ -0,0 +1,257 @@ +// +// String+SwiftyAttributes.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/25/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +import Foundation + +extension String { + + /** + Returns an attributed string with the specified attributes added. + + - parameter attributes: The attributes to add to the new attributed string. + - returns: An `NSMutableAttributedString` with the new attributes applied. + */ + public func withAttributes(_ attributes: [Attribute]) -> NSMutableAttributedString { + return attributedString.withAttributes(attributes) + } + + /** + Returns an attributed string with the specified attribute added. + + - parameter attribute: The attribute to add to the new attributed string. + - returns: An `NSMutableAttributedString` with the new attribute applied. + */ + public func withAttribute(_ attribute: Attribute) -> NSMutableAttributedString { + return attributedString.withAttribute(attribute) + } + + /// Creates a mutable attributed string with the given string. + public var attributedString: NSMutableAttributedString { + return NSMutableAttributedString(string: self) + } + +} + +extension String { + + /** + Creates an attributed string with a specific font. + + - parameter font: The font to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withFont(_ font: Font) -> NSMutableAttributedString { + return withAttribute(.font(font)) + } + + /** + Creates an attributed string with a specific paragraph style. + + - parameter style: The font to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withParagraphStyle(_ style: ParagraphStyle) -> NSMutableAttributedString { + return withAttribute(.paragraphStyle(style)) + } + + /** + Creates an attributed string with a specific text color. + + - parameter color: The text color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withTextColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.textColor(color)) + } + + /** + Creates an attributed string with a specific background color. + + - parameter color: The background color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withBackgroundColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.backgroundColor(color)) + } + + /** + Creates an attributed string with a specific ligature. + + - parameter ligatures: The font to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withLigatures(_ ligatures: Ligatures) -> NSMutableAttributedString { + return withAttribute(.ligatures(ligatures)) + } + + /** + Creates an attributed string with a specific kern. + + - parameter kernValue: The kern value to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withKern(_ kernValue: Double) -> NSMutableAttributedString { + return withAttribute(.kern(kernValue)) + } + + /** + Creates an attributed string with a specific strikethrough style. + + - parameter style: The strikethrough style to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrikethroughStyle(_ style: StrikethroughStyle) -> NSMutableAttributedString { + return withAttribute(.strikethroughStyle(style)) + } + + /** + Creates an attributed string with a specific underline style. + + - parameter style: The underline style to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withUnderlineStyle(_ style: UnderlineStyle) -> NSMutableAttributedString { + return withAttribute(.underlineStyle(style)) + } + + /** + Creates an attributed string with a specific stroke color. + + - parameter color: The stroke color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrokeColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.strokeColor(color)) + } + + /** + Creates an attributed string with a specific stroke width. + + - parameter width: The stroke width to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrokeWidth(_ width: Double) -> NSMutableAttributedString { + return withAttribute(.strokeWidth(width)) + } + + /** + Creates an attributed string with a specific text effect. + + - parameter effect: The text effect to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withTextEffect(_ effect: TextEffect) -> NSMutableAttributedString { + return withAttribute(.textEffect(effect)) + } + + /** + Creates an attributed string with a specific link. + + - parameter link: The URL link to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withLink(_ link: URL) -> NSMutableAttributedString { + return withAttribute(.link(link)) + } + + /** + Creates an attributed string with a specific baseline offset. + + - parameter offset: The baseline offset to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withBaselineOffset(_ offset: Double) -> NSMutableAttributedString { + return withAttribute(.baselineOffset(offset)) + } + + /** + Creates an attributed string with a specific underline color. + + - parameter color: The underline color to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withUnderlineColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.underlineColor(color)) + } + + /** + Creates an attributed string with a specific underline style. + + - parameter color: The underline style to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withStrikethroughColor(_ color: Color) -> NSMutableAttributedString { + return withAttribute(.strikethroughColor(color)) + } + + /** + Creates an attributed string with a specific obliqueness. + + - parameter obliquenessValue: The obliqueness value to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withObliqueness(_ obliquenessValue: Double) -> NSMutableAttributedString { + return withAttribute(.obliqueness(obliquenessValue)) + } + + /** + Creates an attributed string with a specific expansion. + + - parameter expansion: The expansion value to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withExpansion(_ expansion: Double) -> NSMutableAttributedString { + return withAttribute(.expansion(expansion)) + } + + /** + Creates an attributed string with a specific writing direction. + + - parameter directions: The direction(s) to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withWritingDirections(_ directions: [WritingDirection]) -> NSMutableAttributedString { + return withAttribute(.writingDirections(directions)) + } + + /** + Creates an attributed string with a specific vertical glyph form (horizontal or vertical writing direction). + + - parameter form: The horizontal/vertical writing direction to set for the attributed string. See `VerticalGlyphForm` for details. + - returns: A new attributed string with the newly added attribute. + */ + public func withVerticalGlyphForm(_ form: VerticalGlyphForm) -> NSMutableAttributedString { + return withAttribute(.verticalGlyphForm(form)) + } + + #if os(watchOS) + #else + + /** + Creates an attributed string with a specific shadow. + + - parameter shadow: The shadow to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withShadow(_ shadow: Shadow) -> NSMutableAttributedString { + return withAttribute(.shadow(shadow)) + } + + /** + Creates an attributed string with a specific attachment. + + - parameter attachment: The attachment to set for the attributed string. + - returns: A new attributed string with the newly added attribute. + */ + public func withAttachment(_ attachment: TextAttachment) -> NSMutableAttributedString { + return withAttribute(.attachment(attachment)) + } + + #endif + +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/TextEffect.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/TextEffect.swift new file mode 100644 index 0000000..f6562ca --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/TextEffect.swift @@ -0,0 +1,35 @@ +// +// TextEffect.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/28/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +#if os(macOS) + import AppKit +#else + import UIKit +#endif + +/** + An enum describing the possible values for text effects on attributed strings. + */ +public enum TextEffect: RawRepresentable { + + /// A graphical text effect giving glyphs the appearance of letterpress printing, in which type is pressed into the paper. + case letterPressStyle + + public init?(rawValue: String) { + switch rawValue { + case NSTextEffectLetterpressStyle: self = .letterPressStyle + default: return nil + } + } + + public var rawValue: String { + switch self { + case .letterPressStyle: return NSTextEffectLetterpressStyle + } + } +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/VerticalGlyphForm.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/VerticalGlyphForm.swift new file mode 100644 index 0000000..f501f3c --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/VerticalGlyphForm.swift @@ -0,0 +1,23 @@ +// +// VerticalGlyphForm.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 11/16/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +import Foundation + +/** + An enum to indicate horizontal or vertical writing direction. On iOS, only horizontal form is valid. + */ +public enum VerticalGlyphForm: Int { + + /// Horizontal writing direction. + case horizontal = 0 + + #if os(macOS) + /// Vertical writing direction. + case vertical = 1 + #endif +} diff --git a/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/WritingDirection.swift b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/WritingDirection.swift new file mode 100644 index 0000000..8368664 --- /dev/null +++ b/Pods/SwiftyAttributes/SwiftyAttributes/Sources/common/WritingDirection.swift @@ -0,0 +1,57 @@ +// +// WritingDirection.swift +// SwiftyAttributes +// +// Created by Eddie Kaiger on 10/5/16. +// Copyright © 2016 Eddie Kaiger. All rights reserved. +// + +#if os(macOS) + import AppKit +#else + import UIKit +#endif + +@available(iOS 9.0, OSX 10.11, *) +private func mappingValue(direction: NSWritingDirection, formatType: NSWritingDirectionFormatType) -> Int { + return direction.rawValue | formatType.rawValue +} + +/** + An enum that represents a writing direction for an attributed string. Only valid on iOS 9.0+. + */ +public enum WritingDirection: RawRepresentable { + + /// Writing direction is left-to-right. Enables character types with inherent directionality to be overridden when required for special cases, such as for part numbers made of mixed English, digits, and Hebrew letters to be written from right to left. + case leftToRightOverride + + /// Writing direction is right-to-left. Enables character types with inherent directionality to be overridden when required for special cases, such as for part numbers made of mixed English, digits, and Hebrew letters to be written from right to left. + case rightToLeftOverride + + /// Writing direction is left-to-right. Text is embedded in text with another writing direction. For example, an English quotation in the middle of an Arabic sentence could be marked as being embedded left-to-right text. + case leftToRightEmbedding + + /// Writing direction is right-to-left. Text is embedded in text with another writing direction. For example, an English quotation in the middle of an Arabic sentence could be marked as being embedded left-to-right text. + case rightToLeftEmbedding + + public init?(rawValue: Int) { + guard #available(iOS 9.0, macOS 10.11, *) else { return nil } + switch rawValue { + case mappingValue(direction: .leftToRight, formatType: .override): self = .leftToRightOverride + case mappingValue(direction: .rightToLeft, formatType: .override): self = .rightToLeftOverride + case mappingValue(direction: .leftToRight, formatType: .embedding): self = .leftToRightEmbedding + case mappingValue(direction: .rightToLeft, formatType: .embedding): self = .rightToLeftEmbedding + default: return nil + } + } + + public var rawValue: Int { + guard #available(iOS 9.0, macOS 10.11, *) else { return 0 } + switch self { + case .leftToRightOverride: return mappingValue(direction: .leftToRight, formatType: .override) + case .rightToLeftOverride: return mappingValue(direction: .rightToLeft, formatType: .override) + case .leftToRightEmbedding: return mappingValue(direction: .leftToRight, formatType: .embedding) + case .rightToLeftEmbedding: return mappingValue(direction: .rightToLeft, formatType: .embedding) + } + } +} diff --git a/Pods/SwiftyJSON/LICENSE b/Pods/SwiftyJSON/LICENSE new file mode 100644 index 0000000..916a0ac --- /dev/null +++ b/Pods/SwiftyJSON/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/SwiftyJSON/README.md b/Pods/SwiftyJSON/README.md new file mode 100644 index 0000000..6256c84 --- /dev/null +++ b/Pods/SwiftyJSON/README.md @@ -0,0 +1,519 @@ +# SwiftyJSON + +[![Travis CI](https://travis-ci.org/SwiftyJSON/SwiftyJSON.svg?branch=master)](https://travis-ci.org/SwiftyJSON/SwiftyJSON) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![CocoaPods](https://img.shields.io/cocoapods/v/SwiftyJSON.svg) ![Platform](https://img.shields.io/badge/platforms-iOS%208.0+%20%7C%20macOS%2010.10+%20%7C%20tvOS%209.0+%20%7C%20watchOS%202.0+-333333.svg) + +SwiftyJSON makes it easy to deal with JSON data in Swift. + +1. [Why is the typical JSON handling in Swift NOT good](#why-is-the-typical-json-handling-in-swift-not-good) +2. [Requirements](#requirements) +3. [Integration](#integration) +4. [Usage](#usage) + - [Initialization](#initialization) + - [Subscript](#subscript) + - [Loop](#loop) + - [Error](#error) + - [Optional getter](#optional-getter) + - [Non-optional getter](#non-optional-getter) + - [Setter](#setter) + - [Raw object](#raw-object) + - [Literal convertibles](#literal-convertibles) + - [Merging](#merging) +5. [Work with Alamofire](#work-with-alamofire) + +> For Legacy Swift support, take a look at the [swift2 branch](https://github.com/SwiftyJSON/SwiftyJSON/tree/swift2) + +> [中文介绍](http://tangplin.github.io/swiftyjson/) + + +## Why is the typical JSON handling in Swift NOT good? + +Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types. + +Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to Twitter's API https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline). + +The code would look like this: + +```swift +if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]], + let user = statusesArray[0]["user"] as? [String: Any], + let username = user["name"] as? String { + // Finally we got the username +} +``` + +It's not good. + +Even if we use optional chaining, it would be messy: + +```swift +if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]], + let username = (JSONObject[0]["user"] as? [String: Any])?["name"] as? String { + // There's our username +} +``` + +An unreadable mess--for something that should really be simple! + +With SwiftyJSON all you have to do is: + +```swift +let json = JSON(data: dataFromNetworking) +if let userName = json[0]["user"]["name"].string { + //Now you got your value +} +``` + +And don't worry about the Optional Wrapping thing. It's done for you automatically. + +```swift +let json = JSON(data: dataFromNetworking) +if let userName = json[999999]["wrong_key"]["wrong_name"].string { + //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety +} else { + //Print the error + print(json[999999]["wrong_key"]["wrong_name"]) +} +``` + +## Requirements + +- iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+ +- Xcode 8 + +## Integration + +#### CocoaPods (iOS 8+, OS X 10.9+) + +You can use [CocoaPods](http://cocoapods.org/) to install `SwiftyJSON`by adding it to your `Podfile`: + +```ruby +platform :ios, '8.0' +use_frameworks! + +target 'MyApp' do + pod 'SwiftyJSON' +end +``` + +Note that this requires CocoaPods version 36, and your iOS deployment target to be at least 8.0: + + +#### Carthage (iOS 8+, OS X 10.9+) + +You can use [Carthage](https://github.com/Carthage/Carthage) to install `SwiftyJSON` by adding it to your `Cartfile`: + +``` +github "SwiftyJSON/SwiftyJSON" +``` + +#### Swift Package Manager + +You can use [The Swift Package Manager](https://swift.org/package-manager) to install `SwiftyJSON` by adding the proper description to your `Package.swift` file: + +```swift +import PackageDescription + +let package = Package( + name: "YOUR_PROJECT_NAME", + targets: [], + dependencies: [ + .Package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", versions: Version(1,0,0).. = json["list"].arrayValue +``` + +```swift +//If not a Dictionary or nil, return [:] +let user: Dictionary = json["user"].dictionaryValue +``` + +#### Setter + +```swift +json["name"] = JSON("new-name") +json[0] = JSON(1) +``` + +```swift +json["id"].int = 1234567890 +json["coordinate"].double = 8766.766 +json["name"].string = "Jack" +json.arrayObject = [1,2,3,4] +json.dictionaryObject = ["name":"Jack", "age":25] +``` + +#### Raw object + +```swift +let jsonObject: Any = json.object +``` + +```swift +if let jsonObject: Any = json.rawValue +``` + +```swift +//convert the JSON to raw NSData +if let data = json.rawData() { + //Do something you want +} +``` + +```swift +//convert the JSON to a raw String +if let string = json.rawString() { + //Do something you want +} +``` + +#### Existence + +```swift +//shows you whether value specified in JSON or not +if json["name"].exists() +``` + +#### Literal convertibles + +For more info about literal convertibles: [Swift Literal Convertibles](http://nshipster.com/swift-literal-convertible/) + +```swift +//StringLiteralConvertible +let json: JSON = "I'm a json" +``` + +```swift +//IntegerLiteralConvertible +let json: JSON = 12345 +``` + +```swift +//BooleanLiteralConvertible +let json: JSON = true +``` + +```swift +//FloatLiteralConvertible +let json: JSON = 2.8765 +``` + +```swift +//DictionaryLiteralConvertible +let json: JSON = ["I":"am", "a":"json"] +``` + +```swift +//ArrayLiteralConvertible +let json: JSON = ["I", "am", "a", "json"] +``` + +```swift +//NilLiteralConvertible +let json: JSON = nil +``` + +```swift +//With subscript in array +var json: JSON = [1,2,3] +json[0] = 100 +json[1] = 200 +json[2] = 300 +json[999] = 300 //Don't worry, nothing will happen +``` + +```swift +//With subscript in dictionary +var json: JSON = ["name": "Jack", "age": 25] +json["name"] = "Mike" +json["age"] = "25" //It's OK to set String +json["address"] = "L.A." // Add the "address": "L.A." in json +``` + +```swift +//Array & Dictionary +var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]] +json["list"][3]["what"] = "that" +json["list",3,"what"] = "that" +let path: [JSONSubscriptType] = ["list",3,"what"] +json[path] = "that" +``` + +```swift +//With other JSON objects +let user: JSON = ["username" : "Steve", "password": "supersecurepassword"] +let auth: JSON = [ + "user": user.object //use user.object instead of just user + "apikey": "supersecretapitoken" +] +``` + +#### Merging + +It is possible to merge one JSON into another JSON. Merging a JSON into another JSON adds all non existing values to the original JSON which are only present in the `other` JSON. + +If both JSONs contain a value for the same key, _mostly_ this value gets overwritten in the original JSON, but there are two cases where it provides some special treatment: + +- In case of both values being a `JSON.Type.array` the values form the array found in the `other` JSON getting appended to the original JSON's array value. +- In case of both values being a `JSON.Type.dictionary` both JSON-values are getting merged the same way the encapsulating JSON is merged. + +In case, where two fields in a JSON have a different types, the value will get always overwritten. + +There are two different fashions for merging: `merge` modifies the original JSON, whereas `merged` works non-destructively on a copy. + +```swift +let original: JSON = [ + "first_name": "John", + "age": 20, + "skills": ["Coding", "Reading"], + "address": [ + "street": "Front St", + "zip": "12345", + ] +] + +let update: JSON = [ + "last_name": "Doe", + "age": 21, + "skills": ["Writing"], + "address": [ + "zip": "12342", + "city": "New York City" + ] +] + +let updated = original.merge(with: update) +// [ +// "first_name": "John", +// "last_name": "Doe", +// "age": 21, +// "skills": ["Coding", "Reading", "Writing"], +// "address": [ +// "street": "Front St", +// "zip": "12342", +// "city": "New York City" +// ] +// ] +``` + +## String representation +There are two options available: +- use the default Swift one +- use a custom one that will handle optionals well and represent `nil` as `"null"`: +```swift +let dict = ["1":2, "2":"two", "3": nil] as [String: Any?] +let json = JSON(dict) +let representation = json.rawString(options: [.castNilToNSNull: true]) +// representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null} +``` + +## Work with Alamofire + +SwiftyJSON nicely wraps the result of the Alamofire JSON response handler: + +```swift +Alamofire.request(url, method: .get).validate().responseJSON { response in + switch response.result { + case .success(let value): + let json = JSON(value) + print("JSON: \(json)") + case .failure(let error): + print(error) + } +} +``` diff --git a/Pods/SwiftyJSON/Source/SwiftyJSON.swift b/Pods/SwiftyJSON/Source/SwiftyJSON.swift new file mode 100644 index 0000000..e79c10d --- /dev/null +++ b/Pods/SwiftyJSON/Source/SwiftyJSON.swift @@ -0,0 +1,1485 @@ +// SwiftyJSON.swift +// +// Copyright (c) 2014 - 2017 Ruoyu Fu, Pinglin Tang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +// MARK: - Error + +///Error domain +public let ErrorDomain: String = "SwiftyJSONErrorDomain" + +///Error code +public let ErrorUnsupportedType: Int = 999 +public let ErrorIndexOutOfBounds: Int = 900 +public let ErrorWrongType: Int = 901 +public let ErrorNotExist: Int = 500 +public let ErrorInvalidJSON: Int = 490 + +// MARK: - JSON Type + +/** + JSON's type definitions. + + See http://www.json.org + */ +public enum Type :Int{ + + case number + case string + case bool + case array + case dictionary + case null + case unknown +} + +// MARK: - JSON Base +public struct JSON { + + /** + Creates a JSON using the data. + + - parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary + - parameter opt: The JSON serialization reading options. `.AllowFragments` by default. + - parameter error: The NSErrorPointer used to return the error. `nil` by default. + + - returns: The created JSON + */ + public init(data: Data, options opt: JSONSerialization.ReadingOptions = .allowFragments, error: NSErrorPointer = nil) { + do { + let object: Any = try JSONSerialization.jsonObject(with: data, options: opt) + self.init(jsonObject: object) + } catch let aError as NSError { + if error != nil { + error?.pointee = aError + } + self.init(jsonObject: NSNull()) + } + } + + /** + Creates a JSON object + - parameter object: the object + - note: this does not parse a `String` into JSON, instead use `init(parseJSON: String)` + - returns: the created JSON object + */ + public init(_ object: Any) { + switch object { + case let object as [JSON] where object.count > 0: + self.init(array: object) + case let object as [String: JSON] where object.count > 0: + self.init(dictionary: object) + case let object as Data: + self.init(data: object) + default: + self.init(jsonObject: object) + } + } + + /** + Parses the JSON string into a JSON object + - parameter json: the JSON string + - returns: the created JSON object + */ + public init(parseJSON jsonString: String) { + if let data = jsonString.data(using: .utf8) { + self.init(data) + } else { + self.init(NSNull()) + } + } + + /** + Creates a JSON from JSON string + - parameter string: Normal json string like '{"a":"b"}' + + - returns: The created JSON + */ + @available(*, deprecated: 3.2, message: "Use instead `init(parseJSON: )`") + public static func parse(_ json: String) -> JSON { + return json.data(using: String.Encoding.utf8) + .flatMap{ JSON(data: $0) } ?? JSON(NSNull()) + } + + /** + Creates a JSON using the object. + + - parameter object: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity. + + - returns: The created JSON + */ + fileprivate init(jsonObject: Any) { + self.object = jsonObject + } + + /** + Creates a JSON from a [JSON] + + - parameter jsonArray: A Swift array of JSON objects + + - returns: The created JSON + */ + fileprivate init(array: [JSON]) { + self.init(array.map { $0.object }) + } + + /** + Creates a JSON from a [String: JSON] + + - parameter jsonDictionary: A Swift dictionary of JSON objects + + - returns: The created JSON + */ + fileprivate init(dictionary: [String: JSON]) { + var newDictionary = [String: Any](minimumCapacity: dictionary.count) + for (key, json) in dictionary { + newDictionary[key] = json.object + } + + self.init(newDictionary) + } + + /** + Merges another JSON into this JSON, whereas primitive values which are not present in this JSON are getting added, + present values getting overwritten, array values getting appended and nested JSONs getting merged the same way. + + - parameter other: The JSON which gets merged into this JSON + - throws `ErrorWrongType` if the other JSONs differs in type on the top level. + */ + public mutating func merge(with other: JSON) throws { + try self.merge(with: other, typecheck: true) + } + + /** + Merges another JSON into this JSON and returns a new JSON, whereas primitive values which are not present in this JSON are getting added, + present values getting overwritten, array values getting appended and nested JSONS getting merged the same way. + + - parameter other: The JSON which gets merged into this JSON + - returns: New merged JSON + - throws `ErrorWrongType` if the other JSONs differs in type on the top level. + */ + public func merged(with other: JSON) throws -> JSON { + var merged = self + try merged.merge(with: other, typecheck: true) + return merged + } + + // Private woker function which does the actual merging + // Typecheck is set to true for the first recursion level to prevent total override of the source JSON + fileprivate mutating func merge(with other: JSON, typecheck: Bool) throws { + if self.type == other.type { + switch self.type { + case .dictionary: + for (key, _) in other { + try self[key].merge(with: other[key], typecheck: false) + } + case .array: + self = JSON(self.arrayValue + other.arrayValue) + default: + self = other + } + } else { + if typecheck { + throw NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Couldn't merge, because the JSONs differ in type on top level."]) + } else { + self = other + } + } + } + + /// Private object + fileprivate var rawArray: [Any] = [] + fileprivate var rawDictionary: [String : Any] = [:] + fileprivate var rawString: String = "" + fileprivate var rawNumber: NSNumber = 0 + fileprivate var rawNull: NSNull = NSNull() + fileprivate var rawBool: Bool = false + /// Private type + fileprivate var _type: Type = .null + /// prviate error + fileprivate var _error: NSError? = nil + + /// Object in JSON + public var object: Any { + get { + switch self.type { + case .array: + return self.rawArray + case .dictionary: + return self.rawDictionary + case .string: + return self.rawString + case .number: + return self.rawNumber + case .bool: + return self.rawBool + default: + return self.rawNull + } + } + set { + _error = nil + switch newValue { + case let number as NSNumber: + if number.isBool { + _type = .bool + self.rawBool = number.boolValue + } else { + _type = .number + self.rawNumber = number + } + case let string as String: + _type = .string + self.rawString = string + case _ as NSNull: + _type = .null + case _ as [JSON]: + _type = .array + case nil: + _type = .null + case let array as [Any]: + _type = .array + self.rawArray = array + case let dictionary as [String : Any]: + _type = .dictionary + self.rawDictionary = dictionary + default: + _type = .unknown + _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"]) + } + } + } + + /// JSON type + public var type: Type { get { return _type } } + + /// Error in JSON + public var error: NSError? { get { return self._error } } + + /// The static null JSON + @available(*, unavailable, renamed:"null") + public static var nullJSON: JSON { get { return null } } + public static var null: JSON { get { return JSON(NSNull()) } } +} + +public enum Index: Comparable +{ + case array(Int) + case dictionary(DictionaryIndex) + case null + + static public func ==(lhs: Index, rhs: Index) -> Bool { + switch (lhs, rhs) { + case (.array(let left), .array(let right)): + return left == right + case (.dictionary(let left), .dictionary(let right)): + return left == right + case (.null, .null): return true + default: + return false + } + } + + static public func <(lhs: Index, rhs: Index) -> Bool { + switch (lhs, rhs) { + case (.array(let left), .array(let right)): + return left < right + case (.dictionary(let left), .dictionary(let right)): + return left < right + default: + return false + } + } +} + +public typealias JSONIndex = Index +public typealias JSONRawIndex = Index + + +extension JSON: Collection +{ + + public typealias Index = JSONRawIndex + + public var startIndex: Index + { + switch type + { + case .array: + return .array(rawArray.startIndex) + case .dictionary: + return .dictionary(rawDictionary.startIndex) + default: + return .null + } + } + + public var endIndex: Index + { + switch type + { + case .array: + return .array(rawArray.endIndex) + case .dictionary: + return .dictionary(rawDictionary.endIndex) + default: + return .null + } + } + + public func index(after i: Index) -> Index + { + switch i + { + case .array(let idx): + return .array(rawArray.index(after: idx)) + case .dictionary(let idx): + return .dictionary(rawDictionary.index(after: idx)) + default: + return .null + } + + } + + public subscript (position: Index) -> (String, JSON) + { + switch position + { + case .array(let idx): + return (String(idx), JSON(self.rawArray[idx])) + case .dictionary(let idx): + let (key, value) = self.rawDictionary[idx] + return (key, JSON(value)) + default: + return ("", JSON.null) + } + } + + +} + +// MARK: - Subscript + +/** + * To mark both String and Int can be used in subscript. + */ +public enum JSONKey +{ + case index(Int) + case key(String) +} + +public protocol JSONSubscriptType { + var jsonKey:JSONKey { get } +} + +extension Int: JSONSubscriptType { + public var jsonKey:JSONKey { + return JSONKey.index(self) + } +} + +extension String: JSONSubscriptType { + public var jsonKey:JSONKey { + return JSONKey.key(self) + } +} + +extension JSON { + + /// If `type` is `.Array`, return json whose object is `array[index]`, otherwise return null json with error. + fileprivate subscript(index index: Int) -> JSON { + get { + if self.type != .array { + var r = JSON.null + r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"]) + return r + } else if index >= 0 && index < self.rawArray.count { + return JSON(self.rawArray[index]) + } else { + var r = JSON.null + r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"]) + return r + } + } + set { + if self.type == .array { + if self.rawArray.count > index && newValue.error == nil { + self.rawArray[index] = newValue.object + } + } + } + } + + /// If `type` is `.Dictionary`, return json whose object is `dictionary[key]` , otherwise return null json with error. + fileprivate subscript(key key: String) -> JSON { + get { + var r = JSON.null + if self.type == .dictionary { + if let o = self.rawDictionary[key] { + r = JSON(o) + } else { + r._error = NSError(domain: ErrorDomain, code: ErrorNotExist, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] does not exist"]) + } + } else { + r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] failure, It is not an dictionary"]) + } + return r + } + set { + if self.type == .dictionary && newValue.error == nil { + self.rawDictionary[key] = newValue.object + } + } + } + + /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`. + fileprivate subscript(sub sub: JSONSubscriptType) -> JSON { + get { + switch sub.jsonKey { + case .index(let index): return self[index: index] + case .key(let key): return self[key: key] + } + } + set { + switch sub.jsonKey { + case .index(let index): self[index: index] = newValue + case .key(let key): self[key: key] = newValue + } + } + } + + /** + Find a json in the complex data structures by using array of Int and/or String as path. + + - parameter path: The target json's path. Example: + + let json = JSON[data] + let path = [9,"list","person","name"] + let name = json[path] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: [JSONSubscriptType]) -> JSON { + get { + return path.reduce(self) { $0[sub: $1] } + } + set { + switch path.count { + case 0: + return + case 1: + self[sub:path[0]].object = newValue.object + default: + var aPath = path; aPath.remove(at: 0) + var nextJSON = self[sub: path[0]] + nextJSON[aPath] = newValue + self[sub: path[0]] = nextJSON + } + } + } + + /** + Find a json in the complex data structures by using array of Int and/or String as path. + + - parameter path: The target json's path. Example: + + let name = json[9,"list","person","name"] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: JSONSubscriptType...) -> JSON { + get { + return self[path] + } + set { + self[path] = newValue + } + } +} + +// MARK: - LiteralConvertible + +extension JSON: Swift.ExpressibleByStringLiteral { + + public init(stringLiteral value: StringLiteralType) { + self.init(value as Any) + } + + public init(extendedGraphemeClusterLiteral value: StringLiteralType) { + self.init(value as Any) + } + + public init(unicodeScalarLiteral value: StringLiteralType) { + self.init(value as Any) + } +} + +extension JSON: Swift.ExpressibleByIntegerLiteral { + + public init(integerLiteral value: IntegerLiteralType) { + self.init(value as Any) + } +} + +extension JSON: Swift.ExpressibleByBooleanLiteral { + + public init(booleanLiteral value: BooleanLiteralType) { + self.init(value as Any) + } +} + +extension JSON: Swift.ExpressibleByFloatLiteral { + + public init(floatLiteral value: FloatLiteralType) { + self.init(value as Any) + } +} + +extension JSON: Swift.ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, Any)...) { + let array = elements + self.init(dictionaryLiteral: array) + } + + public init(dictionaryLiteral elements: [(String, Any)]) { + let jsonFromDictionaryLiteral: ([String : Any]) -> JSON = { dictionary in + let initializeElement = Array(dictionary.keys).flatMap { key -> (String, Any)? in + if let value = dictionary[key] { + return (key, value) + } + return nil + } + return JSON(dictionaryLiteral: initializeElement) + } + + var dict = [String : Any](minimumCapacity: elements.count) + + for element in elements { + let elementToSet: Any + if let json = element.1 as? JSON { + elementToSet = json.object + } else if let jsonArray = element.1 as? [JSON] { + elementToSet = JSON(jsonArray).object + } else if let dictionary = element.1 as? [String : Any] { + elementToSet = jsonFromDictionaryLiteral(dictionary).object + } else if let dictArray = element.1 as? [[String : Any]] { + let jsonArray = dictArray.map { jsonFromDictionaryLiteral($0) } + elementToSet = JSON(jsonArray).object + } else { + elementToSet = element.1 + } + dict[element.0] = elementToSet + } + + self.init(dict) + } +} + +extension JSON: Swift.ExpressibleByArrayLiteral { + + public init(arrayLiteral elements: Any...) { + self.init(elements as Any) + } +} + +extension JSON: Swift.ExpressibleByNilLiteral { + + @available(*, deprecated, message: "use JSON.null instead. Will be removed in future versions") + public init(nilLiteral: ()) { + self.init(NSNull() as Any) + } +} + +// MARK: - Raw + +extension JSON: Swift.RawRepresentable { + + public init?(rawValue: Any) { + if JSON(rawValue).type == .unknown { + return nil + } else { + self.init(rawValue) + } + } + + public var rawValue: Any { + return self.object + } + + public func rawData(options opt: JSONSerialization.WritingOptions = JSONSerialization.WritingOptions(rawValue: 0)) throws -> Data { + guard JSONSerialization.isValidJSONObject(self.object) else { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "JSON is invalid"]) + } + + return try JSONSerialization.data(withJSONObject: self.object, options: opt) + } + + public func rawString(_ encoding: String.Encoding = .utf8, options opt: JSONSerialization.WritingOptions = .prettyPrinted) -> String? { + do { + return try _rawString(encoding, options: [.jsonSerialization: opt]) + } catch { + print("Could not serialize object to JSON because:", error.localizedDescription) + return nil + } + } + + public func rawString(_ options: [writtingOptionsKeys: Any]) -> String? { + let encoding = options[.encoding] as? String.Encoding ?? String.Encoding.utf8 + let maxObjectDepth = options[.maxObjextDepth] as? Int ?? 10 + do { + return try _rawString(encoding, options: options, maxObjectDepth: maxObjectDepth) + } catch { + print("Could not serialize object to JSON because:", error.localizedDescription) + return nil + } + } + + fileprivate func _rawString( + _ encoding: String.Encoding = .utf8, + options: [writtingOptionsKeys: Any], + maxObjectDepth: Int = 10 + ) throws -> String? { + if (maxObjectDepth < 0) { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "Element too deep. Increase maxObjectDepth and make sure there is no reference loop"]) + } + switch self.type { + case .dictionary: + do { + if !(options[.castNilToNSNull] as? Bool ?? false) { + let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrinted + let data = try self.rawData(options: jsonOption) + return String(data: data, encoding: encoding) + } + + guard let dict = self.object as? [String: Any?] else { + return nil + } + let body = try dict.keys.map { key throws -> String in + guard let value = dict[key] else { + return "\"\(key)\": null" + } + guard let unwrappedValue = value else { + return "\"\(key)\": null" + } + + let nestedValue = JSON(unwrappedValue) + guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "Could not serialize nested JSON"]) + } + if nestedValue.type == .string { + return "\"\(key)\": \"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\"" + } else { + return "\"\(key)\": \(nestedString)" + } + } + + return "{\(body.joined(separator: ","))}" + } catch _ { + return nil + } + case .array: + do { + if !(options[.castNilToNSNull] as? Bool ?? false) { + let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrinted + let data = try self.rawData(options: jsonOption) + return String(data: data, encoding: encoding) + } + + guard let array = self.object as? [Any?] else { + return nil + } + let body = try array.map { value throws -> String in + guard let unwrappedValue = value else { + return "null" + } + + let nestedValue = JSON(unwrappedValue) + guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "Could not serialize nested JSON"]) + } + if nestedValue.type == .string { + return "\"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\"" + } else { + return nestedString + } + } + + return "[\(body.joined(separator: ","))]" + } catch _ { + return nil + } + case .string: + return self.rawString + case .number: + return self.rawNumber.stringValue + case .bool: + return self.rawBool.description + case .null: + return "null" + default: + return nil + } + } +} + +// MARK: - Printable, DebugPrintable + +extension JSON: Swift.CustomStringConvertible, Swift.CustomDebugStringConvertible { + + public var description: String { + if let string = self.rawString(options:.prettyPrinted) { + return string + } else { + return "unknown" + } + } + + public var debugDescription: String { + return description + } +} + +// MARK: - Array + +extension JSON { + + //Optional [JSON] + public var array: [JSON]? { + get { + if self.type == .array { + return self.rawArray.map{ JSON($0) } + } else { + return nil + } + } + } + + //Non-optional [JSON] + public var arrayValue: [JSON] { + get { + return self.array ?? [] + } + } + + //Optional [Any] + public var arrayObject: [Any]? { + get { + switch self.type { + case .array: + return self.rawArray + default: + return nil + } + } + set { + if let array = newValue { + self.object = array as Any + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Dictionary + +extension JSON { + + //Optional [String : JSON] + public var dictionary: [String : JSON]? { + if self.type == .dictionary { + var d = [String : JSON](minimumCapacity: rawDictionary.count) + for (key, value) in rawDictionary { + d[key] = JSON(value) + } + return d + } else { + return nil + } + } + + //Non-optional [String : JSON] + public var dictionaryValue: [String : JSON] { + return self.dictionary ?? [:] + } + + //Optional [String : Any] + + public var dictionaryObject: [String : Any]? { + get { + switch self.type { + case .dictionary: + return self.rawDictionary + default: + return nil + } + } + set { + if let v = newValue { + self.object = v as Any + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Bool + +extension JSON { // : Swift.Bool + + //Optional bool + public var bool: Bool? { + get { + switch self.type { + case .bool: + return self.rawBool + default: + return nil + } + } + set { + if let newValue = newValue { + self.object = newValue as Bool + } else { + self.object = NSNull() + } + } + } + + //Non-optional bool + public var boolValue: Bool { + get { + switch self.type { + case .bool: + return self.rawBool + case .number: + return self.rawNumber.boolValue + case .string: + return ["true", "y", "t"].contains() { (truthyString) in + return self.rawString.caseInsensitiveCompare(truthyString) == .orderedSame + } + default: + return false + } + } + set { + self.object = newValue + } + } +} + +// MARK: - String + +extension JSON { + + //Optional string + public var string: String? { + get { + switch self.type { + case .string: + return self.object as? String + default: + return nil + } + } + set { + if let newValue = newValue { + self.object = NSString(string:newValue) + } else { + self.object = NSNull() + } + } + } + + //Non-optional string + public var stringValue: String { + get { + switch self.type { + case .string: + return self.object as? String ?? "" + case .number: + return self.rawNumber.stringValue + case .bool: + return (self.object as? Bool).map { String($0) } ?? "" + default: + return "" + } + } + set { + self.object = NSString(string:newValue) + } + } +} + +// MARK: - Number +extension JSON { + + //Optional number + public var number: NSNumber? { + get { + switch self.type { + case .number: + return self.rawNumber + case .bool: + return NSNumber(value: self.rawBool ? 1 : 0) + default: + return nil + } + } + set { + self.object = newValue ?? NSNull() + } + } + + //Non-optional number + public var numberValue: NSNumber { + get { + switch self.type { + case .string: + let decimal = NSDecimalNumber(string: self.object as? String) + if decimal == NSDecimalNumber.notANumber { // indicates parse error + return NSDecimalNumber.zero + } + return decimal + case .number: + return self.object as? NSNumber ?? NSNumber(value: 0) + case .bool: + return NSNumber(value: self.rawBool ? 1 : 0) + default: + return NSNumber(value: 0.0) + } + } + set { + self.object = newValue + } + } +} + +//MARK: - Null +extension JSON { + + public var null: NSNull? { + get { + switch self.type { + case .null: + return self.rawNull + default: + return nil + } + } + set { + self.object = NSNull() + } + } + public func exists() -> Bool{ + if let errorValue = error, errorValue.code == ErrorNotExist || + errorValue.code == ErrorIndexOutOfBounds || + errorValue.code == ErrorWrongType { + return false + } + return true + } +} + +//MARK: - URL +extension JSON { + + //Optional URL + public var url: URL? { + get { + switch self.type { + case .string: + // Check for existing percent escapes first to prevent double-escaping of % character + if let _ = self.rawString.range(of: "%[0-9A-Fa-f]{2}", options: .regularExpression, range: nil, locale: nil) { + return Foundation.URL(string: self.rawString) + } else if let encodedString_ = self.rawString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) { + // We have to use `Foundation.URL` otherwise it conflicts with the variable name. + return Foundation.URL(string: encodedString_) + } else { + return nil + } + default: + return nil + } + } + set { + self.object = newValue?.absoluteString ?? NSNull() + } + } +} + +// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64 + +extension JSON { + + public var double: Double? { + get { + return self.number?.doubleValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var doubleValue: Double { + get { + return self.numberValue.doubleValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var float: Float? { + get { + return self.number?.floatValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var floatValue: Float { + get { + return self.numberValue.floatValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int: Int? + { + get + { + return self.number?.intValue + } + set + { + if let newValue = newValue + { + self.object = NSNumber(value: newValue) + } else + { + self.object = NSNull() + } + } + } + + public var intValue: Int { + get { + return self.numberValue.intValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt: UInt? { + get { + return self.number?.uintValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uIntValue: UInt { + get { + return self.numberValue.uintValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int8: Int8? { + get { + return self.number?.int8Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: Int(newValue)) + } else { + self.object = NSNull() + } + } + } + + public var int8Value: Int8 { + get { + return self.numberValue.int8Value + } + set { + self.object = NSNumber(value: Int(newValue)) + } + } + + public var uInt8: UInt8? { + get { + return self.number?.uint8Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt8Value: UInt8 { + get { + return self.numberValue.uint8Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int16: Int16? { + get { + return self.number?.int16Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int16Value: Int16 { + get { + return self.numberValue.int16Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt16: UInt16? { + get { + return self.number?.uint16Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt16Value: UInt16 { + get { + return self.numberValue.uint16Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int32: Int32? { + get { + return self.number?.int32Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int32Value: Int32 { + get { + return self.numberValue.int32Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt32: UInt32? { + get { + return self.number?.uint32Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt32Value: UInt32 { + get { + return self.numberValue.uint32Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int64: Int64? { + get { + return self.number?.int64Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int64Value: Int64 { + get { + return self.numberValue.int64Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt64: UInt64? { + get { + return self.number?.uint64Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt64Value: UInt64 { + get { + return self.numberValue.uint64Value + } + set { + self.object = NSNumber(value: newValue) + } + } +} + +//MARK: - Comparable +extension JSON : Swift.Comparable {} + +public func ==(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber == rhs.rawNumber + case (.string, .string): + return lhs.rawString == rhs.rawString + case (.bool, .bool): + return lhs.rawBool == rhs.rawBool + case (.array, .array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.dictionary, .dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.null, .null): + return true + default: + return false + } +} + +public func <=(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber <= rhs.rawNumber + case (.string, .string): + return lhs.rawString <= rhs.rawString + case (.bool, .bool): + return lhs.rawBool == rhs.rawBool + case (.array, .array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.dictionary, .dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.null, .null): + return true + default: + return false + } +} + +public func >=(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber >= rhs.rawNumber + case (.string, .string): + return lhs.rawString >= rhs.rawString + case (.bool, .bool): + return lhs.rawBool == rhs.rawBool + case (.array, .array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.dictionary, .dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.null, .null): + return true + default: + return false + } +} + +public func >(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber > rhs.rawNumber + case (.string, .string): + return lhs.rawString > rhs.rawString + default: + return false + } +} + +public func <(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber < rhs.rawNumber + case (.string, .string): + return lhs.rawString < rhs.rawString + default: + return false + } +} + +private let trueNumber = NSNumber(value: true) +private let falseNumber = NSNumber(value: false) +private let trueObjCType = String(cString: trueNumber.objCType) +private let falseObjCType = String(cString: falseNumber.objCType) + +// MARK: - NSNumber: Comparable + +extension NSNumber { + var isBool:Bool { + get { + let objCType = String(cString: self.objCType) + if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType){ + return true + } else { + return false + } + } + } +} + +func ==(lhs: NSNumber, rhs: NSNumber) -> Bool { + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == .orderedSame + } +} + +func !=(lhs: NSNumber, rhs: NSNumber) -> Bool { + return !(lhs == rhs) +} + +func <(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == .orderedAscending + } +} + +func >(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == ComparisonResult.orderedDescending + } +} + +func <=(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != .orderedDescending + } +} + +func >=(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != .orderedAscending + } +} + +public enum writtingOptionsKeys { + case jsonSerialization + case castNilToNSNull + case maxObjextDepth + case encoding +} diff --git a/Pods/Target Support Files/Kingfisher/Info.plist b/Pods/Target Support Files/Kingfisher/Info.plist new file mode 100644 index 0000000..078bbf3 --- /dev/null +++ b/Pods/Target Support Files/Kingfisher/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.5.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m b/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m new file mode 100644 index 0000000..1b89d0e --- /dev/null +++ b/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Kingfisher : NSObject +@end +@implementation PodsDummy_Kingfisher +@end diff --git a/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch b/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h b/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h new file mode 100644 index 0000000..89b88ac --- /dev/null +++ b/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h @@ -0,0 +1,17 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "Kingfisher.h" + +FOUNDATION_EXPORT double KingfisherVersionNumber; +FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; + diff --git a/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap b/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap new file mode 100644 index 0000000..2a20d91 --- /dev/null +++ b/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap @@ -0,0 +1,6 @@ +framework module Kingfisher { + umbrella header "Kingfisher-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig b/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig new file mode 100644 index 0000000..6e8fd9e --- /dev/null +++ b/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig @@ -0,0 +1,12 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kingfisher +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_LDFLAGS = -framework "CFNetwork" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 3.0 diff --git a/Pods/Target Support Files/ObjectMapper/Info.plist b/Pods/Target Support Files/ObjectMapper/Info.plist new file mode 100644 index 0000000..086f7e6 --- /dev/null +++ b/Pods/Target Support Files/ObjectMapper/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.2.3 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m b/Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m new file mode 100644 index 0000000..7033cce --- /dev/null +++ b/Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_ObjectMapper : NSObject +@end +@implementation PodsDummy_ObjectMapper +@end diff --git a/Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch b/Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h b/Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h new file mode 100644 index 0000000..e993e40 --- /dev/null +++ b/Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double ObjectMapperVersionNumber; +FOUNDATION_EXPORT const unsigned char ObjectMapperVersionString[]; + diff --git a/Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap b/Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap new file mode 100644 index 0000000..539c248 --- /dev/null +++ b/Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap @@ -0,0 +1,6 @@ +framework module ObjectMapper { + umbrella header "ObjectMapper-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/ObjectMapper/ObjectMapper.xcconfig b/Pods/Target Support Files/ObjectMapper/ObjectMapper.xcconfig new file mode 100644 index 0000000..c2b8915 --- /dev/null +++ b/Pods/Target Support Files/ObjectMapper/ObjectMapper.xcconfig @@ -0,0 +1,11 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/ObjectMapper +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/ObjectMapper +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 3.0 diff --git a/Pods/Target Support Files/Pods-App/Info.plist b/Pods/Target Support Files/Pods-App/Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.markdown b/Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.markdown new file mode 100644 index 0000000..f73c705 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.markdown @@ -0,0 +1,153 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Kingfisher + +The MIT License (MIT) + +Copyright (c) 2017 Wei Wang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +## ObjectMapper + +The MIT License (MIT) +Copyright (c) 2014 Hearst + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +## SCLAlertView + +Copyright (c) 2013-2014 SCPopUpView by Viktor Radchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## SwiftOverlays + +The MIT License (MIT) + +Copyright (c) 2015 Peter Prokop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## SwiftyAttributes + +The MIT License (MIT) + +Copyright (c) 2015 Eddie Kaiger + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## SwiftyJSON + +The MIT License (MIT) + +Copyright (c) 2016 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## Toaster + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2013-2015 Suyeol Jeon + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + +Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.plist b/Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.plist new file mode 100644 index 0000000..5ef22f9 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App-acknowledgements.plist @@ -0,0 +1,221 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2017 Wei Wang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + License + MIT + Title + Kingfisher + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) +Copyright (c) 2014 Hearst + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + License + MIT + Title + ObjectMapper + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2013-2014 SCPopUpView by Viktor Radchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + SCLAlertView + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Peter Prokop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + License + MIT + Title + SwiftOverlays + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2015 Eddie Kaiger + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + License + MIT + Title + SwiftyAttributes + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2016 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + SwiftyJSON + Type + PSGroupSpecifier + + + FooterText + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2013-2015 Suyeol Jeon <devxoul@gmail.com> + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + + License + WTPFL + Title + Toaster + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods-App/Pods-App-dummy.m b/Pods/Target Support Files/Pods-App/Pods-App-dummy.m new file mode 100644 index 0000000..ac0b6a1 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_App : NSObject +@end +@implementation PodsDummy_Pods_App +@end diff --git a/Pods/Target Support Files/Pods-App/Pods-App-frameworks.sh b/Pods/Target Support Files/Pods-App/Pods-App-frameworks.sh new file mode 100755 index 0000000..21a2f08 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App-frameworks.sh @@ -0,0 +1,111 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # use filter instead of exclude so missing patterns dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" + install_framework "$BUILT_PRODUCTS_DIR/ObjectMapper/ObjectMapper.framework" + install_framework "$BUILT_PRODUCTS_DIR/SCLAlertView/SCLAlertView.framework" + install_framework "$BUILT_PRODUCTS_DIR/SwiftOverlays/SwiftOverlays.framework" + install_framework "$BUILT_PRODUCTS_DIR/SwiftyAttributes/SwiftyAttributes.framework" + install_framework "$BUILT_PRODUCTS_DIR/SwiftyJSON/SwiftyJSON.framework" + install_framework "$BUILT_PRODUCTS_DIR/Toaster/Toaster.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" + install_framework "$BUILT_PRODUCTS_DIR/ObjectMapper/ObjectMapper.framework" + install_framework "$BUILT_PRODUCTS_DIR/SCLAlertView/SCLAlertView.framework" + install_framework "$BUILT_PRODUCTS_DIR/SwiftOverlays/SwiftOverlays.framework" + install_framework "$BUILT_PRODUCTS_DIR/SwiftyAttributes/SwiftyAttributes.framework" + install_framework "$BUILT_PRODUCTS_DIR/SwiftyJSON/SwiftyJSON.framework" + install_framework "$BUILT_PRODUCTS_DIR/Toaster/Toaster.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Pods/Target Support Files/Pods-App/Pods-App-resources.sh b/Pods/Target Support Files/Pods-App/Pods-App-resources.sh new file mode 100755 index 0000000..4602c68 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App-resources.sh @@ -0,0 +1,99 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi diff --git a/Pods/Target Support Files/Pods-App/Pods-App-umbrella.h b/Pods/Target Support Files/Pods-App/Pods-App-umbrella.h new file mode 100644 index 0000000..e6a69e5 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_AppVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_AppVersionString[]; + diff --git a/Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig b/Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig new file mode 100644 index 0000000..241b4a9 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig @@ -0,0 +1,10 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" "$PODS_CONFIGURATION_BUILD_DIR/SCLAlertView" "$PODS_CONFIGURATION_BUILD_DIR/SwiftOverlays" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyAttributes" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON" "$PODS_CONFIGURATION_BUILD_DIR/Toaster" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper/ObjectMapper.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SCLAlertView/SCLAlertView.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftOverlays/SwiftOverlays.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyAttributes/SwiftyAttributes.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON/SwiftyJSON.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Toaster/Toaster.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "ObjectMapper" -framework "SCLAlertView" -framework "SwiftOverlays" -framework "SwiftyAttributes" -framework "SwiftyJSON" -framework "Toaster" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Pods/Target Support Files/Pods-App/Pods-App.modulemap b/Pods/Target Support Files/Pods-App/Pods-App.modulemap new file mode 100644 index 0000000..f4aba19 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App.modulemap @@ -0,0 +1,6 @@ +framework module Pods_App { + umbrella header "Pods-App-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig b/Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig new file mode 100644 index 0000000..241b4a9 --- /dev/null +++ b/Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig @@ -0,0 +1,10 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" "$PODS_CONFIGURATION_BUILD_DIR/SCLAlertView" "$PODS_CONFIGURATION_BUILD_DIR/SwiftOverlays" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyAttributes" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON" "$PODS_CONFIGURATION_BUILD_DIR/Toaster" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper/ObjectMapper.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SCLAlertView/SCLAlertView.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftOverlays/SwiftOverlays.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyAttributes/SwiftyAttributes.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON/SwiftyJSON.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Toaster/Toaster.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "ObjectMapper" -framework "SCLAlertView" -framework "SwiftOverlays" -framework "SwiftyAttributes" -framework "SwiftyJSON" -framework "Toaster" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Pods/Target Support Files/SCLAlertView/Info.plist b/Pods/Target Support Files/SCLAlertView/Info.plist new file mode 100644 index 0000000..75ba4d0 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.7.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m b/Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m new file mode 100644 index 0000000..27c49dd --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SCLAlertView : NSObject +@end +@implementation PodsDummy_SCLAlertView +@end diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch b/Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h b/Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h new file mode 100644 index 0000000..95b14c3 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SCLAlertViewVersionNumber; +FOUNDATION_EXPORT const unsigned char SCLAlertViewVersionString[]; + diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap b/Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap new file mode 100644 index 0000000..0f04947 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap @@ -0,0 +1,6 @@ +framework module SCLAlertView { + umbrella header "SCLAlertView-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig b/Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig new file mode 100644 index 0000000..0b6c1dc --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SCLAlertView +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SCLAlertView +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/SwiftOverlays/Info.plist b/Pods/Target Support Files/SwiftOverlays/Info.plist new file mode 100644 index 0000000..4522675 --- /dev/null +++ b/Pods/Target Support Files/SwiftOverlays/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-dummy.m b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-dummy.m new file mode 100644 index 0000000..366f546 --- /dev/null +++ b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftOverlays : NSObject +@end +@implementation PodsDummy_SwiftOverlays +@end diff --git a/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-prefix.pch b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-umbrella.h b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-umbrella.h new file mode 100644 index 0000000..4de1c26 --- /dev/null +++ b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftOverlaysVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftOverlaysVersionString[]; + diff --git a/Pods/Target Support Files/SwiftOverlays/SwiftOverlays.modulemap b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays.modulemap new file mode 100644 index 0000000..c8a814d --- /dev/null +++ b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays.modulemap @@ -0,0 +1,6 @@ +framework module SwiftOverlays { + umbrella header "SwiftOverlays-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftOverlays/SwiftOverlays.xcconfig b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays.xcconfig new file mode 100644 index 0000000..552c0ba --- /dev/null +++ b/Pods/Target Support Files/SwiftOverlays/SwiftOverlays.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftOverlays +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftOverlays +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/SwiftyAttributes/Info.plist b/Pods/Target Support Files/SwiftyAttributes/Info.plist new file mode 100644 index 0000000..90db36a --- /dev/null +++ b/Pods/Target Support Files/SwiftyAttributes/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-dummy.m b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-dummy.m new file mode 100644 index 0000000..236293e --- /dev/null +++ b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftyAttributes : NSObject +@end +@implementation PodsDummy_SwiftyAttributes +@end diff --git a/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-prefix.pch b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-umbrella.h b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-umbrella.h new file mode 100644 index 0000000..ee2c2c2 --- /dev/null +++ b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftyAttributesVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftyAttributesVersionString[]; + diff --git a/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.modulemap b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.modulemap new file mode 100644 index 0000000..3435050 --- /dev/null +++ b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.modulemap @@ -0,0 +1,6 @@ +framework module SwiftyAttributes { + umbrella header "SwiftyAttributes-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.xcconfig b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.xcconfig new file mode 100644 index 0000000..13c8d8e --- /dev/null +++ b/Pods/Target Support Files/SwiftyAttributes/SwiftyAttributes.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftyAttributes +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyAttributes +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/SwiftyJSON/Info.plist b/Pods/Target Support Files/SwiftyJSON/Info.plist new file mode 100644 index 0000000..36f2c7e --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.1.4 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m new file mode 100644 index 0000000..3159bec --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftyJSON : NSObject +@end +@implementation PodsDummy_SwiftyJSON +@end diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h new file mode 100644 index 0000000..b627dec --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftyJSONVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[]; + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap new file mode 100644 index 0000000..6f41751 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap @@ -0,0 +1,6 @@ +framework module SwiftyJSON { + umbrella header "SwiftyJSON-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig new file mode 100644 index 0000000..07e3ce3 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig @@ -0,0 +1,11 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyJSON +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 3.0 diff --git a/Pods/Target Support Files/Toaster/Info.plist b/Pods/Target Support Files/Toaster/Info.plist new file mode 100644 index 0000000..763f9a6 --- /dev/null +++ b/Pods/Target Support Files/Toaster/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.0.3 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Toaster/Toaster-dummy.m b/Pods/Target Support Files/Toaster/Toaster-dummy.m new file mode 100644 index 0000000..69744ae --- /dev/null +++ b/Pods/Target Support Files/Toaster/Toaster-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Toaster : NSObject +@end +@implementation PodsDummy_Toaster +@end diff --git a/Pods/Target Support Files/Toaster/Toaster-prefix.pch b/Pods/Target Support Files/Toaster/Toaster-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/Toaster/Toaster-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/Toaster/Toaster-umbrella.h b/Pods/Target Support Files/Toaster/Toaster-umbrella.h new file mode 100644 index 0000000..09c15c7 --- /dev/null +++ b/Pods/Target Support Files/Toaster/Toaster-umbrella.h @@ -0,0 +1,17 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "Toaster.h" + +FOUNDATION_EXPORT double ToasterVersionNumber; +FOUNDATION_EXPORT const unsigned char ToasterVersionString[]; + diff --git a/Pods/Target Support Files/Toaster/Toaster.modulemap b/Pods/Target Support Files/Toaster/Toaster.modulemap new file mode 100644 index 0000000..dd6c141 --- /dev/null +++ b/Pods/Target Support Files/Toaster/Toaster.modulemap @@ -0,0 +1,6 @@ +framework module Toaster { + umbrella header "Toaster-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Toaster/Toaster.xcconfig b/Pods/Target Support Files/Toaster/Toaster.xcconfig new file mode 100644 index 0000000..81e4954 --- /dev/null +++ b/Pods/Target Support Files/Toaster/Toaster.xcconfig @@ -0,0 +1,12 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Toaster +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_LDFLAGS = -framework "Foundation" -framework "QuartzCore" -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Toaster +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +SWIFT_VERSION = 3.0 diff --git a/Pods/Toaster/LICENSE b/Pods/Toaster/LICENSE new file mode 100644 index 0000000..92897e7 --- /dev/null +++ b/Pods/Toaster/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2013-2015 Suyeol Jeon + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/Pods/Toaster/README.md b/Pods/Toaster/README.md new file mode 100644 index 0000000..a15cb7b --- /dev/null +++ b/Pods/Toaster/README.md @@ -0,0 +1,118 @@ +Toaster +======= + +[![Build Status](https://travis-ci.org/devxoul/Toaster.svg?branch=master)](https://travis-ci.org/devxoul/Toaster) +![Swift](https://img.shields.io/badge/Swift-3.0-orange.svg) +[![CocoaPods](http://img.shields.io/cocoapods/v/Toaster.svg?style=flat)](http://cocoapods.org/?q=name%3AToaster%20author%3Adevxoul) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +Android-like toast with very simple interface. (formerly JLToast) + +Toaster is written in Swift 3.0. If you're looking for Swift 2 compatible version, see the [JLToast 1.4.2](https://github.com/devxoul/JLToast/tree/1.4.2). + + +Features +-------- + +- **Queueing**: Centralized toast center manages the toast queue. +- **Customizable**: See the [Appearance](#appearance) section. + + +At a Glance +----------- + +```swift +import Toaster + +Toast(text: "Hello, world!").show() +``` + + +Installation +------------ + +- **For iOS 8+ projects with [CocoaPods](https://cocoapods.org):** + + ```ruby + pod 'Toaster', '~> 2.0' + ``` + +- **For iOS 8+ projects with [Carthage](https://github.com/Carthage/Carthage):** + + ``` + github "devxoul/Toaster" ~> 2.0 + ``` + +- **For iOS 7 projects:** I recommend you to try [CocoaSeeds](https://github.com/devxoul/CocoaSeeds), which uses source code instead of dynamic frameworks. Sample Seedfile: + + ```ruby + github 'devxoul/Toaster', '2.0.3', :files => 'Sources/*.{swift,h}' + ``` + + +Getting Started +--------------- + +### Setting Duration and Delay + +```swift +Toast(text: "Hello, world!", duration: Delay.long) +Toast(text: "Hello, world!", delay: Delay.short, duration: Delay.long) +``` + +### Removing Toasts + +- **Removing toast with reference**: + + ```swift + let toast = Toast(text: "Hello") + toast.show() + toast.cancel() // remove toast immediately + ``` + +- **Removing current toast**: + + ```swift + if let currentToast = ToastCenter.default.currentToast { + currentToast.cancel() + } + ``` + +- **Removing all toasts**: + + ```swift + ToastCenter.default.cancelAll() + ``` + +### Appearance + +Since Toaster 2.0.0, you can use `UIAppearance` to set default appearance. This is an short example to set default background color to red. + +```swift +ToastView.appearance().backgroundColor = .red +``` + + +Supported appearance properties are: + +| Property | Type | Description | +|---|---|---| +| `backgroundColor` | `UIColor` | Background color | +| `cornerRadius` | `CGFloat` | Corner radius | +| `textInsets` | `UIEdgeInsets` | Text inset | +| `textColor` | `UIColor` | Text color | +| `font` | `UIFont` | Font | +| `bottomOffsetPortrait` | `CGFloat` | Vertical offfset from bottom in portrait mode | +|` bottomOffsetLandscape` | `CGFloat` | Vertical offfset from bottom in landscape mode | + + +Screenshots +----------- + +![Toaster Screenshot](https://raw.github.com/devxoul/Toaster/master/Screenshots/Toaster.png) + + +License +------- + +Toaster is under [WTFPL](http://www.wtfpl.net/). You can do what the fuck you want with Toast. See [LICENSE](LICENSE) file for more info. diff --git a/Pods/Toaster/Sources/Toast.swift b/Pods/Toaster/Sources/Toast.swift new file mode 100644 index 0000000..ce76021 --- /dev/null +++ b/Pods/Toaster/Sources/Toast.swift @@ -0,0 +1,169 @@ +/* + * Toast.swift + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) 2013-2015 Su Yeol Jeon + * + * Everyone is permitted to copy and distribute verbatim or modified + * copies of this license document, and changing it is allowed as long + * as the name is changed. + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + * 0. You just DO WHAT THE FUCK YOU WANT TO. + * + */ + +import UIKit + +public struct Delay { + public static let short: TimeInterval = 2.0 + public static let long: TimeInterval = 3.5 +} + +open class Toast: Operation { + + // MARK: Properties + + public var text: String? { + get { return self.view.text } + set { self.view.text = newValue } + } + + public var delay: TimeInterval + public var duration: TimeInterval + + private var _executing = false + override open var isExecuting: Bool { + get { + return self._executing + } + set { + self.willChangeValue(forKey: "isExecuting") + self._executing = newValue + self.didChangeValue(forKey: "isExecuting") + } + } + + private var _finished = false + override open var isFinished: Bool { + get { + return self._finished + } + set { + self.willChangeValue(forKey: "isFinished") + self._finished = newValue + self.didChangeValue(forKey: "isFinished") + } + } + + + // MARK: UI + + public var view: ToastView = ToastView() + + + // MARK: Initializing + + public init(text: String?, delay: TimeInterval = 0, duration: TimeInterval = Delay.short) { + self.delay = delay + self.duration = duration + super.init() + self.text = text + } + + + // MARK: Factory (Deprecated) + + @available(*, deprecated, message: "Use 'init(text:)' instead.") + public class func makeText(_ text: String) -> Toast { + return Toast(text: text) + } + + @available(*, deprecated, message: "Use 'init(text:duration:)' instead.") + public class func makeText(_ text: String, duration: TimeInterval) -> Toast { + return Toast(text: text, duration: duration) + } + + @available(*, deprecated, message: "Use 'init(text:delay:duration:)' instead.") + public class func makeText(_ text: String?, delay: TimeInterval, duration: TimeInterval) -> Toast { + return Toast(text: text, delay: delay, duration: duration) + } + + + // MARK: Showing + + public func show() { + ToastCenter.default.add(self) + } + + + // MARK: Cancelling + + open override func cancel() { + super.cancel() + self.finish() + self.view.removeFromSuperview() + } + + + // MARK: Operation Subclassing + + override open func start() { + guard Thread.isMainThread else { + DispatchQueue.main.async { [weak self] in + self?.start() + } + return + } + super.start() + } + + override open func main() { + self.isExecuting = true + + DispatchQueue.main.async { + self.view.setNeedsLayout() + self.view.alpha = 0 + ToastWindow.shared.addSubview(self.view) + + UIView.animate( + withDuration: 0.5, + delay: self.delay, + options: .beginFromCurrentState, + animations: { + self.view.alpha = 1 + }, + completion: { completed in + UIView.animate( + withDuration: self.duration, + animations: { + self.view.alpha = 1.0001 + }, + completion: { completed in + self.finish() + UIView.animate( + withDuration: 0.5, + animations: { + self.view.alpha = 0 + }, + completion: { completed in + self.view.removeFromSuperview() + } + ) + } + ) + } + ) + } + } + + open func finish() { + self.isExecuting = false + self.isFinished = true + } + +} diff --git a/Pods/Toaster/Sources/ToastCenter.swift b/Pods/Toaster/Sources/ToastCenter.swift new file mode 100644 index 0000000..62ee55d --- /dev/null +++ b/Pods/Toaster/Sources/ToastCenter.swift @@ -0,0 +1,75 @@ +/* + * ToastCenter.swift + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) 2013-2015 Su Yeol Jeon + * + * Everyone is permitted to copy and distribute verbatim or modified + * copies of this license document, and changing it is allowed as long + * as the name is changed. + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + * 0. You just DO WHAT THE FUCK YOU WANT TO. + * + */ + +import UIKit + +open class ToastCenter { + + // MARK: Properties + + private let queue: OperationQueue = { + let queue = OperationQueue() + queue.maxConcurrentOperationCount = 1 + return queue + }() + + open var currentToast: Toast? { + return self.queue.operations.first as? Toast + } + + open static let `default` = ToastCenter() + + + // MARK: Initializing + + init() { + NotificationCenter.default.addObserver( + self, + selector: #selector(self.deviceOrientationDidChange), + name: .UIDeviceOrientationDidChange, + object: nil + ) + } + + + // MARK: Adding Toasts + + open func add(_ toast: Toast) { + self.queue.addOperation(toast) + } + + + // MARK: Cancelling Toasts + + open func cancelAll() { + for toast in self.queue.operations { + toast.cancel() + } + } + + + // MARK: Notifications + + dynamic func deviceOrientationDidChange() { + if let lastToast = self.queue.operations.first as? Toast { + lastToast.view.setNeedsLayout() + } + } + +} diff --git a/Pods/Toaster/Sources/ToastView.swift b/Pods/Toaster/Sources/ToastView.swift new file mode 100644 index 0000000..5ce04e0 --- /dev/null +++ b/Pods/Toaster/Sources/ToastView.swift @@ -0,0 +1,187 @@ +/* + * ToastView.swift + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) 2013-2015 Su Yeol Jeon + * + * Everyone is permitted to copy and distribute verbatim or modified + * copies of this license document, and changing it is allowed as long + * as the name is changed. + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + * 0. You just DO WHAT THE FUCK YOU WANT TO. + * + */ + +import UIKit + +open class ToastView: UIView { + + // MARK: Properties + + open var text: String? { + get { return self.textLabel.text } + set { self.textLabel.text = newValue } + } + + + // MARK: Appearance + + /// The background view's color. + override open dynamic var backgroundColor: UIColor? { + get { return self.backgroundView.backgroundColor } + set { self.backgroundView.backgroundColor = newValue } + } + + /// The background view's corner radius. + open dynamic var cornerRadius: CGFloat { + get { return self.backgroundView.layer.cornerRadius } + set { self.backgroundView.layer.cornerRadius = newValue } + } + + /// The inset of the text label. + open dynamic var textInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10) + + /// The color of the text label's text. + open dynamic var textColor: UIColor? { + get { return self.textLabel.textColor } + set { self.textLabel.textColor = newValue } + } + + /// The font of the text label. + open dynamic var font: UIFont? { + get { return self.textLabel.font } + set { self.textLabel.font = newValue } + } + + /// The bottom offset from the screen's bottom in portrait mode. + open dynamic var bottomOffsetPortrait: CGFloat = { + switch UIDevice.current.userInterfaceIdiom { + case .unspecified: return 30 + case .phone: return 30 + case .pad: return 60 + case .tv: return 90 + case .carPlay: return 30 + } + }() + + /// The bottom offset from the screen's bottom in landscape mode. + open dynamic var bottomOffsetLandscape: CGFloat = { + switch UIDevice.current.userInterfaceIdiom { + case .unspecified: return 20 + case .phone: return 20 + case .pad: return 40 + case .tv: return 60 + case .carPlay: return 20 + } + }() + + + // MARK: UI + + private let backgroundView: UIView = { + let `self` = UIView() + self.backgroundColor = UIColor(white: 0, alpha: 0.7) + self.layer.cornerRadius = 5 + self.clipsToBounds = true + return self + }() + private let textLabel: UILabel = { + let `self` = UILabel() + self.textColor = .white + self.backgroundColor = .clear + self.font = { + switch UIDevice.current.userInterfaceIdiom { + case .unspecified: return .systemFont(ofSize: 12) + case .phone: return .systemFont(ofSize: 12) + case .pad: return .systemFont(ofSize: 16) + case .tv: return .systemFont(ofSize: 20) + case .carPlay: return .systemFont(ofSize: 12) + } + }() + self.numberOfLines = 0 + self.textAlignment = .center + return self + }() + + + // MARK: Initializing + + public init() { + super.init(frame: .zero) + self.isUserInteractionEnabled = false + self.addSubview(self.backgroundView) + self.addSubview(self.textLabel) + } + + required convenience public init?(coder aDecoder: NSCoder) { + self.init() + } + + + // MARK: Layout + + override open func layoutSubviews() { + super.layoutSubviews() + let containerSize = ToastWindow.shared.frame.size + let constraintSize = CGSize( + width: containerSize.width * (280.0 / 320.0), + height: CGFloat.greatestFiniteMagnitude + ) + let textLabelSize = self.textLabel.sizeThatFits(constraintSize) + self.textLabel.frame = CGRect( + x: self.textInsets.left, + y: self.textInsets.top, + width: textLabelSize.width, + height: textLabelSize.height + ) + self.backgroundView.frame = CGRect( + x: 0, + y: 0, + width: self.textLabel.frame.size.width + self.textInsets.left + self.textInsets.right, + height: self.textLabel.frame.size.height + self.textInsets.top + self.textInsets.bottom + ) + + var x: CGFloat + var y: CGFloat + var width: CGFloat + var height: CGFloat + + let orientation = UIApplication.shared.statusBarOrientation + if orientation.isPortrait || !ToastWindow.shared.shouldRotateManually { + width = containerSize.width + height = containerSize.height + y = self.bottomOffsetPortrait + } else { + width = containerSize.height + height = containerSize.width + y = self.bottomOffsetLandscape + } + + let backgroundViewSize = self.backgroundView.frame.size + x = (width - backgroundViewSize.width) * 0.5 + y = height - (backgroundViewSize.height + y) + self.frame = CGRect( + x: x, + y: y, + width: backgroundViewSize.width, + height: backgroundViewSize.height + ) + } + + override open func hitTest(_ point: CGPoint, with event: UIEvent!) -> UIView? { + if let superview = self.superview { + let pointInWindow = self.convert(point, to: superview) + let contains = self.frame.contains(pointInWindow) + if contains && self.isUserInteractionEnabled { + return self + } + } + return nil + } + +} diff --git a/Pods/Toaster/Sources/ToastWindow.swift b/Pods/Toaster/Sources/ToastWindow.swift new file mode 100644 index 0000000..ffdece7 --- /dev/null +++ b/Pods/Toaster/Sources/ToastWindow.swift @@ -0,0 +1,155 @@ +/* + * ToastView.swift + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) 2013-2015 Su Yeol Jeon + * + * Everyone is permitted to copy and distribute verbatim or modified + * copies of this license document, and changing it is allowed as long + * as the name is changed. + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + * 0. You just DO WHAT THE FUCK YOU WANT TO. + * + */ + +import UIKit + +open class ToastWindow: UIWindow { + + open static let shared = ToastWindow(frame: UIScreen.main.bounds) + + /// Will not return `rootViewController` while this value is `true`. Or the rotation will be fucked in iOS 9. + var isStatusBarOrientationChanging = false + + /// Don't rotate manually if the application: + /// + /// - is running on iPad + /// - is running on iOS 9 + /// - supports all orientations + /// - doesn't require full screen + /// - has launch storyboard + /// + var shouldRotateManually: Bool { + let iPad = UIDevice.current.userInterfaceIdiom == .pad + let application = UIApplication.shared + let window = application.delegate?.window ?? nil + let supportsAllOrientations = application.supportedInterfaceOrientations(for: window) == .all + + let info = Bundle.main.infoDictionary + let requiresFullScreen = (info?["UIRequiresFullScreen"] as? NSNumber)?.boolValue == true + let hasLaunchStoryboard = info?["UILaunchStoryboardName"] != nil + + if #available(iOS 9, *), iPad && supportsAllOrientations && !requiresFullScreen && hasLaunchStoryboard { + return false + } + return true + } + + override open var rootViewController: UIViewController? { + get { + guard !self.isStatusBarOrientationChanging else { return nil } + guard let firstWindow = UIApplication.shared.windows.first else { return nil } + return firstWindow is ToastWindow ? nil : firstWindow.rootViewController + } + set { /* Do nothing */ } + } + + public override init(frame: CGRect) { + super.init(frame: frame) + self.isUserInteractionEnabled = false + self.windowLevel = CGFloat.greatestFiniteMagnitude + self.backgroundColor = .clear + self.isHidden = false + self.handleRotate(UIApplication.shared.statusBarOrientation) + + NotificationCenter.default.addObserver( + self, + selector: #selector(self.bringWindowToTop), + name: .UIWindowDidBecomeVisible, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(self.statusBarOrientationWillChange), + name: .UIApplicationWillChangeStatusBarOrientation, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(self.statusBarOrientationDidChange), + name: .UIApplicationDidChangeStatusBarOrientation, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(self.applicationDidBecomeActive), + name: .UIApplicationDidBecomeActive, + object: nil + ) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + /// Bring ToastWindow to top when another window is being shown. + func bringWindowToTop(_ notification: Notification) { + if !(notification.object is ToastWindow) { + ToastWindow.shared.isHidden = true + ToastWindow.shared.isHidden = false + } + } + + dynamic func statusBarOrientationWillChange() { + self.isStatusBarOrientationChanging = true + } + + dynamic func statusBarOrientationDidChange() { + let orientation = UIApplication.shared.statusBarOrientation + self.handleRotate(orientation) + self.isStatusBarOrientationChanging = false + } + + func applicationDidBecomeActive() { + let orientation = UIApplication.shared.statusBarOrientation + self.handleRotate(orientation) + } + + func handleRotate(_ orientation: UIInterfaceOrientation) { + let angle = self.angleForOrientation(orientation) + if self.shouldRotateManually { + self.transform = CGAffineTransform(rotationAngle: CGFloat(angle)) + } + + if let window = UIApplication.shared.windows.first { + if orientation.isPortrait || !self.shouldRotateManually { + self.frame.size.width = window.bounds.size.width + self.frame.size.height = window.bounds.size.height + } else { + self.frame.size.width = window.bounds.size.height + self.frame.size.height = window.bounds.size.width + } + } + + self.frame.origin = .zero + + DispatchQueue.main.async { + ToastCenter.default.currentToast?.view.setNeedsLayout() + } + } + + func angleForOrientation(_ orientation: UIInterfaceOrientation) -> Double { + switch orientation { + case .landscapeLeft: return -.pi / 2 + case .landscapeRight: return .pi / 2 + case .portraitUpsideDown: return .pi + default: return 0 + } + } + +} diff --git a/Pods/Toaster/Sources/Toaster.h b/Pods/Toaster/Sources/Toaster.h new file mode 100644 index 0000000..977b478 --- /dev/null +++ b/Pods/Toaster/Sources/Toaster.h @@ -0,0 +1,21 @@ +/* + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) 2013-2015 Suyeol Jeon + * + * Everyone is permitted to copy and distribute verbatim or modified + * copies of this license document, and changing it is allowed as long + * as the name is changed. + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + * 0. You just DO WHAT THE FUCK YOU WANT TO. + * + */ + +#import + +FOUNDATION_EXPORT double ToasterVersionNumber; +FOUNDATION_EXPORT const unsigned char ToasterVersionString[]; -- 1.8.5.3

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 literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/Contents.json b/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/App/Assets.xcassets/add_icon.imageset/Contents.json b/App/Assets.xcassets/add_icon.imageset/Contents.json new file mode 100644 index 0000000..fa24f10 --- /dev/null +++ b/App/Assets.xcassets/add_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "add_icon.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/App/Assets.xcassets/add_icon.imageset/add_icon.png b/App/Assets.xcassets/add_icon.imageset/add_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e01083c58a17819df8795bd481f1acf72bd8befe GIT binary patch literal 716 zcmV;-0yF)IP)Px#32;bRa{vGr5&!@f5&>tQ(oz5b0&z)1K~zW$rB=;r6G0SzZ+9a!fg)1;IH)JR zX@yFu2W?T%BwMIW+Z^=hO+Az}|RwQ8ej|d0v|y=LoH#=^-c6n|^?kUI?kT1VvQI zE7Y&SS#*G<0hP(l9L|(GA@(_#t*h=r{+{6jFywB^Euj>nVWYSU1vvb)~sL!NUCfb>A4_D6g`WykdZQC=62t&cloFcOC1myYo{0WpJ$Q1HP z2ANOh%lRV~`lXPJDJI&cqJ>ir#F!JQL42Ht7u+9U>CsG&@{ct%yTcBpQ)wfw{#~V( zw2Z95`Pw-R(e3ix5t8j_A*1Zx)@^Vc2q%j35vCm$?vuGuagLqnF^80c*7=3tU7BF| z&$5PQ-)dP*007SX(-Dy+BrxO~>bCCf-^d#SH%;u!hY~fvHH{ntK3S;Es~BSbG>ft%1KU&Mwvt77w8RX%I;^ zBo6y+!h*J7nZh&Yzq@8d667kmK@6U|NN~t_#sQJ{#A}zUqt7=rekA!i54%sYr`fW` yQN3f(?Z?-JzZfgnzuk*9X}X58m7D1R0000Px#32;bRa{vGr5&!@f5&>tQ(oz5b0(MD6K~zW$#a7SjVNn!64`~=SroO0&k_8)O z!BQ5+a!N{3lGQBK?4)F2IkT3jnI_p#7XF0EZi-@MvZKb2Ny!3DN|<}(n)~qHeVS?J zlA%TCE>R(YF*( zvJyVZYlFe?bF<`yZ`~Cg{n}clXhd@rYAs@elN+=8>X-KmelAD0sbIFq_Sw)9HX*CCp|s z=yto%Xf&YL>nYi{Ngxe$)h-iS)2 zf=nhOn_4C7B~H9vB`0O`L?RLJ`~56*qjL-630SFCs}KwZ!EU#M!{LB%I1IH~jisFQ zSS)sfu>kj~v5}}?(R@BnP1x;r(C_z8-}G=eKr;FJ+53FH3-6x7Ju!AK6)Y?zl}bhA zV&g=Pv%xKlPln29G@|OwIvfr~m|m|J+1CcQFuqeN*sW5cR*R?adcBsSo^a_*=P(=& zRGdyHRd3d7-()hm;jjR=Fis>YsZ@%pH)}eb7Ga%EM`T|cYBH6I*C1;zmrF<_5+}&x z@xas56O6}WSglr|)oQ`zazQSagLpj7hMhWnu~>*O)=)GuOQq6jBC&yTI(B%WP>`bl z_rw_8YPG;-vw_>~mQ99EELtoU0pGQpC^|7sfMXCR5)KD^r}$*Bd#BSWwA*c{*XuAC z3@BZ8eWulx23owu@>F5@I^00000NkvXXu0mjf D7&c80 literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/delete_ic.imageset/Contents.json b/App/Assets.xcassets/delete_ic.imageset/Contents.json new file mode 100644 index 0000000..6fb89fc --- /dev/null +++ b/App/Assets.xcassets/delete_ic.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "delete_ic.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/App/Assets.xcassets/delete_ic.imageset/delete_ic.png b/App/Assets.xcassets/delete_ic.imageset/delete_ic.png new file mode 100644 index 0000000000000000000000000000000000000000..0b3a928bbdffab8d95cd6b56f9e5bfeb3b45c25e GIT binary patch literal 977 zcmV;?11|iDP)Px#32;bRa{vGr5&!@f5&>tQ(oz5b19nM7K~zW$tyWt|R8bV&*XQVjHjVaCNSfA1 zkol+~sgDFR(x^TRr23Kil3t)hSs{w_{P!b*2r8k#kFu-`!XBn#7+6`P6{SU`_{eF- zX7sk!&2ybHtI4}y&DnRKbM|7NbLQ-$$zP!Bx(!3}z;$3Im;}avbr*#1`)qXZZnv5GVS;O|ZGY z228<;Wo**N#%v#V>=O6&TWUV=WW4D&&!=V7a*VpQ?me<-6J=09UTT)7m?Wv9FDCL7B!a^+fJ z%=HvpPLKCddtirUwzs)0FqVraCy^fCo40ICB!B8Q?O=`Wxyj}fU-xU%hOv+t@*!zvtwmT^kys0KT1 zD>B5ff3F%aqc#rpFd}84iqQh&96z|Etj^4`5OmKe*)1%fGr^p)5{cQmMTOvvvK<=6 z|J@K|4;;MK3qT*MwWD+o-N6BWa%a_HaqKfnALsf&zH7LD2BususE_N>!^e!>)6^n; z2P@=T=Zhbs|L_}`wp8MdSBNcRnHt5+D8@r+Vu~PpAi4xi4VVgBJa_KN$klrBwzZ4D zr+4z1n3y2k_gNeBBx==4PF3KZaT(3bRL8U}gu*~RYWcYRSG>f)BGBBbUj=@fVc;N4 z=Jm%S&86nG!K25ER|f|`X3-4RfsM$D{%P?YR|#_<@gHsD00000NkvXXu0mjf-V)DB literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/icon.imageset/Contents.json b/App/Assets.xcassets/icon.imageset/Contents.json new file mode 100644 index 0000000..d15d754 --- /dev/null +++ b/App/Assets.xcassets/icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon.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/App/Assets.xcassets/icon.imageset/icon.png b/App/Assets.xcassets/icon.imageset/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..7512e3f6318d4fd33d6c8d4141c821e15f61080c GIT binary patch literal 20748 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 P00000NkvXXu0mjfrG)<- literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/next_arrow_white.imageset/Contents.json b/App/Assets.xcassets/next_arrow_white.imageset/Contents.json new file mode 100644 index 0000000..ae3c27c --- /dev/null +++ b/App/Assets.xcassets/next_arrow_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "next_arrow_white.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/App/Assets.xcassets/next_arrow_white.imageset/next_arrow_white.png b/App/Assets.xcassets/next_arrow_white.imageset/next_arrow_white.png new file mode 100644 index 0000000000000000000000000000000000000000..9b73b511b58e25b2f719784c5937a8b1bcb65c14 GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(eqEmV3H5hIkymdexAx*+7Ij;BbII`OBUotP$1zV$;0dPTjRJ#9-#c)+e*0 zT$LmP=h@l&1qs~KIQ#WKvlY(@)yC@0PhCD8oz?NIjx}C~p{mD8l=J5~{*@sU|EUOB z#wvZ%73x@VT2U}B*yV_WgNDPj3k^IAnWR5H(e3**vGAP!3}2J&7kMU0rhYb#~f9}40N(;iEBhjN@7W>RdP`( zkYX@0Ff`RQG|)A)3^A~@GBvg`vCuUzu`)3Dt60AZMMG|WN@iLmM1z5eu90Dgk)f3_ YP>(r8L);?{7oY|PPgg&ebxsLQ0D%N~p8x;= literal 0 HcmV?d00001 diff --git a/App/Assets.xcassets/prev_arrow_white.imageset/Contents.json b/App/Assets.xcassets/prev_arrow_white.imageset/Contents.json new file mode 100644 index 0000000..25de2df --- /dev/null +++ b/App/Assets.xcassets/prev_arrow_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "prev_arrow_white.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/App/Assets.xcassets/prev_arrow_white.imageset/prev_arrow_white.png b/App/Assets.xcassets/prev_arrow_white.imageset/prev_arrow_white.png new file mode 100644 index 0000000000000000000000000000000000000000..1a715a9ce33956145c0f1af153f96af3bc39fbe2 GIT binary patch literal 468 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8wRq zgi1h|@m`vI1yJy#r;B5V$MLmS_xd_J3a~ub%E>cHRcGQyDTk7VjDw7}3NC_b3ps_n z98^LYgvA>s&S5fCVwxE7>qO=Ee>K~uzkPG&<-4@+Zc6&cn6s8wGKKCrrZn+-{@FDL zuhuV-ue~p%<69;uQO%OQ+3J@21HPISK^f+M*=j6(AIE<2{_uLw%0+YSwjaIbTI?(R z_YD7zyxU78tv+(*-R8f^B2&%&J$CvWKlv&~|8Hf2Gb-4$+3)Ik{(Q%nBlFeZ=7KUm z_n*olJB~1&k2v7Lx<+QxY|WJI#V6!66swnrO02q>Y@1TAqZo2e=he@P4fiXh?tgtE z(3tXvF}i=w)YB_oKLdJRwZt`|BqgyV)hf9t6-Y4{85o-C8XD*tT80={TA3PKnHcFB zm{=JYL2GQ_0#(6JLgCxj?;QX|b^2DN4 lhVt@qz0ADq;^f4FRK5J7^x5xhq=1STJYD@<);T3K0RSe>tyTa4 literal 0 HcmV?d00001 diff --git a/App/Base.lproj/Main.storyboard b/App/Base.lproj/Main.storyboard new file mode 100644 index 0000000..50ac960 --- /dev/null +++ b/App/Base.lproj/Main.storyboard @@ -0,0 +1,1647 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/Info.plist b/App/Info.plist new file mode 100644 index 0000000..f22edf7 --- /dev/null +++ b/App/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + MOROOKA + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + FirebaseAutomaticScreenReportingEnabled + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/App/Localizable.strings b/App/Localizable.strings new file mode 100644 index 0000000..d6ef2fd --- /dev/null +++ b/App/Localizable.strings @@ -0,0 +1,2 @@ +khong_de_trong = "Không được để trống"; +muc_chup_da_ton_tai = "Mục chụp đã tồn tại"; \ No newline at end of file diff --git a/App/SplashController.swift b/App/SplashController.swift new file mode 100644 index 0000000..620d6de --- /dev/null +++ b/App/SplashController.swift @@ -0,0 +1,23 @@ +import UIKit +import Foundation + +class SplashController: UIViewController { + @IBOutlet weak var vContent: UIView! + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + VCRoot.openController(self) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + self.view.removeAllChild() + self.view.removeFromSuperview() + } + + +} diff --git a/App/commons/VCContainerFullScreen.swift b/App/commons/VCContainerFullScreen.swift new file mode 100644 index 0000000..21d0d81 --- /dev/null +++ b/App/commons/VCContainerFullScreen.swift @@ -0,0 +1,19 @@ +import UIKit +import Foundation + +class VCContainerFullScreen: UIViewController, IContainerController { + var currentViewController: UIViewController? + +// static func openController(_ viewController: UIViewController) { +// let vcOpen = VCContainerFullScreen() +// viewController.present(vcOpen, animated: true, completion: nil) +// } + + func getContainerview() -> UIView{ + return self.view + } + + override var prefersStatusBarHidden : Bool { + return true + } +} diff --git a/App/commons/VCHome.swift b/App/commons/VCHome.swift new file mode 100644 index 0000000..7055b51 --- /dev/null +++ b/App/commons/VCHome.swift @@ -0,0 +1,34 @@ +import UIKit + +class VCHome: UIViewController { + + static func getInstance() -> VCHome { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCHome") as! VCHome + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + } + + @IBAction func btnCreateNew(_ sender: Any) { + VCRootCreateNew.openController(self) + } + + @IBAction func btnListCtruong(_ sender: Any) { + VCRootListCtruong.openController(self) + } + + @IBAction func btnDangKyBenDatHang(_ sender: Any) { + } + + @IBAction func btnConfirmIdClick(_ sender: Any) { + } + + func getVcRoot() -> VCRoot? { + return self.parent as? VCRoot + } + +} + diff --git a/App/commons/VCRoot.swift b/App/commons/VCRoot.swift new file mode 100644 index 0000000..dbda86f --- /dev/null +++ b/App/commons/VCRoot.swift @@ -0,0 +1,16 @@ +import UIKit +import Foundation + +class VCRoot: VCContainerFullScreen { + var id: String? + static func openController(_ viewController: UIViewController) { + let vcOpen = VCRoot() + viewController.present(vcOpen, animated: true, completion: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + changeCurrentController(VCInputId.getInstance()) //TODO: neu chua co id -> Ra cai nay +// changeCurrentController(VCHome.getInstance()) + } +} diff --git a/App/commons/VTopLogo.swift b/App/commons/VTopLogo.swift new file mode 100644 index 0000000..030e932 --- /dev/null +++ b/App/commons/VTopLogo.swift @@ -0,0 +1,37 @@ +import Foundation +import UIKit + +@IBDesignable class VTopLogo: UIView{ + @IBOutlet weak var imvLogo: UIImageView! + + var leftBtnClick: (() -> Void)? + + override init(frame: CGRect) { + super.init(frame: frame) + xibSetup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + xibSetup() + } + + func xibSetup() { + let bundle = Bundle(for: type(of: self)) + let nib = UINib(nibName: "VTopLogo", bundle: bundle) + let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView + view.frame = bounds + view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight] + addSubview(view) + + let tap = UITapGestureRecognizer.init(target: self, action: #selector(VTopLogo.logoClick)) + imvLogo.addGestureRecognizer(tap) + } + + func logoClick() { + leftBtnClick?() +// else{ +// self.parentViewController?.dismiss(animated: true, completion: nil) +// } + } +} diff --git a/App/commons/VTopLogo.xib b/App/commons/VTopLogo.xib new file mode 100644 index 0000000..39da715 --- /dev/null +++ b/App/commons/VTopLogo.xib @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/createNew/CellChonMucChup.swift b/App/createNew/CellChonMucChup.swift new file mode 100644 index 0000000..23230e8 --- /dev/null +++ b/App/createNew/CellChonMucChup.swift @@ -0,0 +1,45 @@ +import Foundation +import UIKit + +class CellChonMucChup: BaseTableViewCellUI { + var tvTitle: UILabel! + var mucChup: MucChup! + + static func registerClass(tableView: UITableView, forCellReuseIdentifier: String) { + tableView.register(CellChonMucChup.self, forCellReuseIdentifier: forCellReuseIdentifier) + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + initView() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initView() + } + + private func initView() { + tvTitle = UILabel() + tvTitle.translatesAutoresizingMaskIntoConstraints = false + tvTitle.numberOfLines = 0 + self.contentView.addSubview(tvTitle) + NSLayoutConstraint.activate([tvTitle.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), + tvTitle.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 8), + tvTitle.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -8), + tvTitle.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)]) + + } + + override func configCellWithData(baseObj: Any, index: Int) { + super.configCellWithData(baseObj: baseObj, index: index) + mucChup = baseObj as! MucChup + tvTitle.text = mucChup.name + + if mucChup.isSelected == true { + self.contentView.backgroundColor = UIColor(hexString: "#1C9CF6") + } else { + self.contentView.backgroundColor = UIColor.white + } + } +} diff --git a/App/createNew/CellMucChupWithCamera.swift b/App/createNew/CellMucChupWithCamera.swift new file mode 100644 index 0000000..045a70f --- /dev/null +++ b/App/createNew/CellMucChupWithCamera.swift @@ -0,0 +1,53 @@ +import Foundation +import UIKit + +class CellMucChupWithCamera: BaseTableViewCellUI { + var tvTitle: UILabel!, imv: UIImageView! + var mucChup: MucChup! + + static func registerClass(tableView: UITableView, forCellReuseIdentifier: String) { + tableView.register(CellMucChupWithCamera.self, forCellReuseIdentifier: forCellReuseIdentifier) + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + initView() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initView() + } + + private func initView() { + let stackView = UIStackView() + stackView.axis = UILayoutConstraintAxis.horizontal + stackView.spacing = 8 + stackView.translatesAutoresizingMaskIntoConstraints = false + self.contentView.addSubview(stackView) + NSLayoutConstraint.activate([stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15), + stackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 15), + stackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -15), + stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15)]) + tvTitle = UILabel() + tvTitle.numberOfLines = 0 + stackView.addArrangedSubview(tvTitle) + imv = UIImageView() + imv.contentMode = .scaleAspectFit + imv.isUserInteractionEnabled = true + stackView.addArrangedSubview(imv) + imv.widthAnchor.constraint(equalToConstant: 33).isActive = true + imv.image = #imageLiteral(resourceName:"camera_black_ic") + let tap = UITapGestureRecognizer(target: self, action: #selector(CellMucChupWithCamera.btnImvClick)) + imv.addGestureRecognizer(tap) + } + + func btnImvClick() { + } + + override func configCellWithData(baseObj: Any, index: Int) { + super.configCellWithData(baseObj: baseObj, index: index) + mucChup = baseObj as! MucChup + tvTitle.text = mucChup.name + } +} diff --git a/App/createNew/CellMucChupWithDelete.swift b/App/createNew/CellMucChupWithDelete.swift new file mode 100644 index 0000000..4e5e29b --- /dev/null +++ b/App/createNew/CellMucChupWithDelete.swift @@ -0,0 +1,56 @@ +import Foundation +import UIKit + +class CellMucChupWithDelete: BaseTableViewCellUI { + var tvTitle: UILabel!, btnDelete: UIImageView! + var mucChup: MucChup! + + static func registerClass(tableView: UITableView, forCellReuseIdentifier: String) { + tableView.register(CellMucChupWithDelete.self, forCellReuseIdentifier: forCellReuseIdentifier) + } + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + initView() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initView() + } + + private func initView() { + let stackView = UIStackView() + stackView.axis = UILayoutConstraintAxis.horizontal + stackView.spacing = 8 + stackView.translatesAutoresizingMaskIntoConstraints = false + self.contentView.addSubview(stackView) + NSLayoutConstraint.activate([stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), + stackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 8), + stackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -8), + stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)]) + tvTitle = UILabel() + tvTitle.numberOfLines = 0 + stackView.addArrangedSubview(tvTitle) + btnDelete = UIImageView() + btnDelete.contentMode = .scaleAspectFit + btnDelete.isUserInteractionEnabled = true + stackView.addArrangedSubview(btnDelete) + btnDelete.widthAnchor.constraint(equalToConstant: 30).isActive = true + btnDelete.image = #imageLiteral(resourceName:"delete_ic") + let tap = UITapGestureRecognizer(target: self, action: #selector(CellMucChupWithDelete.btnDeleteClick)) + btnDelete.addGestureRecognizer(tap) + } + + func btnDeleteClick() { + if let vcNhapMucChup = self.viewController as? VCNhapMucChup { + vcNhapMucChup.deleteMucChup(mucChup) + } + } + + override func configCellWithData(baseObj: Any, index: Int) { + super.configCellWithData(baseObj: baseObj, index: index) + mucChup = baseObj as! MucChup + tvTitle.text = mucChup.name + } +} diff --git a/App/createNew/VCChonMucChup.swift b/App/createNew/VCChonMucChup.swift new file mode 100644 index 0000000..cc50f9e --- /dev/null +++ b/App/createNew/VCChonMucChup.swift @@ -0,0 +1,68 @@ +import UIKit + +class VCChonMucChup: UIViewController, IVCLoadDataTableViewUIThread { + @IBOutlet weak var topMargin: NSLayoutConstraint! + @IBOutlet weak var tableView: UITableViewLoadDataFromUIThread! + + static func getInstance() -> VCChonMucChup { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCChonMucChup") as! VCChonMucChup + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + tableView.emptyText = "Empty" + CellChonMucChup.registerClass(tableView: tableView, forCellReuseIdentifier: "CellChonMucChup") + self.tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine + self.tableView.rowHeight = UITableViewAutomaticDimension + self.tableView.estimatedRowHeight = 150 + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.initAndLoadData(self) + } + + @IBAction func btnRightClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCConfirmMucDaChon.getInstance()) + } + + @IBAction func btnLeftClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCNhapMucChup.getInstance()) + } + + //region ============== TableView Data ========= + + func loadDataOnUI(complete: @escaping ([Any]?) -> ()) { + complete(getVcRoot()?.mucChups) + } + + func getAllCell() -> [BaseCell] { + var baseCells: [BaseCell] = [BaseCell](); + baseCells.append(BaseCell(type: 0, identifier: "CellChonMucChup")) + return baseCells + } + + func getTypeOfData(baseobj: Any) -> Int { + return 0 + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let mucChup = self.tableView.getItem(indexPath) as? MucChup { + if mucChup.isSelected == true { + mucChup.isSelected = nil + } else { + mucChup.isSelected = true + } + self.tableView.reloadRows(at: [indexPath], with: .none) + } + } + + //endregion + + func getVcRoot() -> VCRootCreateNew? { + return self.parent as? VCRootCreateNew + } +} + diff --git a/App/createNew/VCConfirmMucDaChon.swift b/App/createNew/VCConfirmMucDaChon.swift new file mode 100644 index 0000000..a2b0988 --- /dev/null +++ b/App/createNew/VCConfirmMucDaChon.swift @@ -0,0 +1,69 @@ +import UIKit + +class VCConfirmMucDaChon: UIViewController, IVCLoadDataTableViewUIThread { + @IBOutlet weak var topMargin: NSLayoutConstraint! + @IBOutlet weak var tableView: UITableViewLoadDataFromUIThread! + + static func getInstance() -> VCConfirmMucDaChon { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCConfirmMucDaChon") as! VCConfirmMucDaChon + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + tableView.emptyText = "Empty" + CellMucChupWithCamera.registerClass(tableView: tableView, forCellReuseIdentifier: "CellMucChupWithCamera") + self.tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine + self.tableView.allowsSelection = false + self.tableView.rowHeight = UITableViewAutomaticDimension + self.tableView.estimatedRowHeight = 150 + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.initAndLoadData(self) + } + + @IBAction func btnRightClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCSettingBienQC.getInstance()) + } + + @IBAction func btnLeftClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCChonMucChup.getInstance()) + } + + //region ============== TableView Data ========= + + func loadDataOnUI(complete: @escaping ([Any]?) -> ()) { + if let list = getVcRoot()?.mucChups { + var mucChups = [MucChup]() + for item in list { + if item.isSelected == true { mucChups.append(item) } + } + complete(mucChups) + } else { + complete(nil) + } + } + + func getAllCell() -> [BaseCell] { + var baseCells: [BaseCell] = [BaseCell](); + baseCells.append(BaseCell(type: 0, identifier: "CellMucChupWithCamera")) + return baseCells + } + + func getTypeOfData(baseobj: Any) -> Int { + return 0 + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + } + + //endregion + + func getVcRoot() -> VCRootCreateNew? { + return self.parent as? VCRootCreateNew + } +} + diff --git a/App/createNew/VCNhapMucChup.swift b/App/createNew/VCNhapMucChup.swift new file mode 100644 index 0000000..fd7ec8d --- /dev/null +++ b/App/createNew/VCNhapMucChup.swift @@ -0,0 +1,111 @@ +import UIKit + +class VCNhapMucChup: UIViewController, IVCLoadDataTableViewUIThread { + @IBOutlet weak var edtTitle: UITextField! + @IBOutlet weak var topMargin: NSLayoutConstraint! + @IBOutlet weak var tableView: UITableViewLoadDataFromUIThread! + + static func getInstance() -> VCNhapMucChup { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCNhapMucChup") as! VCNhapMucChup + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + tableView.emptyText = "Empty" + CellMucChupWithDelete.registerClass(tableView: tableView, forCellReuseIdentifier: "CellMucChupWithDelete") + self.tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine + self.tableView.rowHeight = UITableViewAutomaticDimension + self.tableView.estimatedRowHeight = 150 + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.initAndLoadData(self) + } + + @IBAction func btnAddClick(_ sender: Any) { + if let title = edtTitle.text?.trimAndReturn(), title.length > 0 { + for item in tableView.itemsData { + if let mc = item as? MucChup, mc.name == title { + CommonUtils.showToastLong(text: LocalizedString("muc_chup_da_ton_tai")) + return + } + } + tableView.itemsData.insert(MucChup(name: title), at: 0) + tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) + tableView.removeAllLoaddingEmpty() + edtTitle.text = "" + } else { + CommonUtils.showToastLong(text: LocalizedString("khong_de_trong")) + } + } + + @IBAction func btnRightClick(_ sender: Any) { + saveMucChup() + _ = getVcRoot()?.changeCurrentController(VCChonMucChup.getInstance()) + } + + @IBAction func btnLeftClick(_ sender: Any) { + saveMucChup() + _ = getVcRoot()?.changeCurrentController(VCNhapTenCtruong.getInstance()) + } + + private func saveMucChup() { + var mucChups = [MucChup]() + for item in tableView.itemsData { + if let mucChup = item as? MucChup { + mucChups.append(mucChup) + } + } + getVcRoot()?.mucChups = mucChups + } + //region ============== TableView Data ========= + + func loadDataOnUI(complete: @escaping ([Any]?) -> ()) { + complete(getVcRoot()?.mucChups) + } + + func getAllCell() -> [BaseCell] { + var baseCells: [BaseCell] = [BaseCell](); + baseCells.append(BaseCell(type: 0, identifier: "CellMucChupWithDelete")) + return baseCells + } + + func getTypeOfData(baseobj: Any) -> Int { + return 0 + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let mucChup = self.tableView.getItem(indexPath) as? MucChup { + edtTitle.text = mucChup.name + } + } + + func deleteMucChup(_ mucChupParam: MucChup) { + var indexFind = -1, index = -1 + for item in self.tableView.itemsData { + index += 1 + if let mucChup = item as? MucChup, mucChup.name == mucChupParam.name { + indexFind = index + break + } + } + if indexFind >= 0 { + tableView.itemsData.remove(at: indexFind) + tableView.deleteRows(at: [IndexPath(row: indexFind, section: 0)], with: .automatic) + } + } + //endregion + + + @IBAction func edtActionTrigerClick(_ sender: Any) { + edtTitle.endEditing(true) + } + + func getVcRoot() -> VCRootCreateNew? { + return self.parent as? VCRootCreateNew + } +} + diff --git a/App/createNew/VCNhapTenCtruong.swift b/App/createNew/VCNhapTenCtruong.swift new file mode 100644 index 0000000..7d45ea5 --- /dev/null +++ b/App/createNew/VCNhapTenCtruong.swift @@ -0,0 +1,41 @@ +import UIKit + +class VCNhapTenCtruong: UIViewController { + @IBOutlet weak var edtTitle: UITextField! + @IBOutlet weak var topMargin: NSLayoutConstraint! + + static func getInstance() -> VCNhapTenCtruong { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCNhapTenCtruong") as! VCNhapTenCtruong + return vcOpen + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + edtTitle.text = getVcRoot()?.tenCtruong + } + + @IBAction func btnLeftClick(_ sender: Any) { + getVcRoot()?.tenCtruong = edtTitle.text + _ = getVcRoot()?.changeCurrentController(VCNhapTenCty.getInstance()) + } + + @IBAction func btnRightClick(_ sender: Any) { + getVcRoot()?.tenCtruong = edtTitle.text + _ = getVcRoot()?.changeCurrentController(VCNhapMucChup.getInstance()) + } + + @IBAction func edtActionTrigerClick(_ sender: Any) { + edtTitle.endEditing(true) + } + + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + } + + func getVcRoot() -> VCRootCreateNew? { + return self.parent as? VCRootCreateNew + } +} + diff --git a/App/createNew/VCNhapTenCty.swift b/App/createNew/VCNhapTenCty.swift new file mode 100644 index 0000000..8693e48 --- /dev/null +++ b/App/createNew/VCNhapTenCty.swift @@ -0,0 +1,35 @@ +import UIKit + +class VCNhapTenCty: UIViewController { + @IBOutlet weak var edtTenCty: UITextField! + @IBOutlet weak var topMargin: NSLayoutConstraint! + + static func getInstance() -> VCNhapTenCty { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCNhapTenCty") as! VCNhapTenCty + return vcOpen + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + edtTenCty.text = getVcRoot()?.tenCty + } + + @IBAction func btnLeftClick(_ sender: Any) { + getVcRoot()?.dismiss(animated: true) + } + + @IBAction func btnRightClick(_ sender: Any) { + getVcRoot()?.tenCty = edtTenCty.text + _ = getVcRoot()?.changeCurrentController(VCNhapTenCtruong.getInstance()) + } + + @IBAction func edtActionTrigerClick(_ sender: Any) { + edtTenCty.endEditing(true) + } + + func getVcRoot() -> VCRootCreateNew? { + return self.parent as? VCRootCreateNew + } +} + diff --git a/App/createNew/VCRootCreateNew.swift b/App/createNew/VCRootCreateNew.swift new file mode 100644 index 0000000..12bb3c0 --- /dev/null +++ b/App/createNew/VCRootCreateNew.swift @@ -0,0 +1,17 @@ +import UIKit +import Foundation + +class VCRootCreateNew: VCContainerFullScreen { + var tenCty: String?, tenCtruong: String? + var mucChups: [MucChup]? + + static func openController(_ viewController: UIViewController) { + let vcOpen = VCRootCreateNew() + viewController.present(vcOpen, animated: true, completion: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + changeCurrentController(VCNhapTenCty.getInstance()) + } +} diff --git a/App/createNew/VCSettingBienQC.swift b/App/createNew/VCSettingBienQC.swift new file mode 100644 index 0000000..8e72cd7 --- /dev/null +++ b/App/createNew/VCSettingBienQC.swift @@ -0,0 +1,29 @@ +import UIKit + +class VCSettingBienQC: UIViewController { + @IBOutlet weak var topMargin: NSLayoutConstraint! + + static func getInstance() -> VCSettingBienQC { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCSettingBienQC") as! VCSettingBienQC + return vcOpen + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + } + + @IBAction func btnLeftClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCConfirmMucDaChon.getInstance()) + } + + @IBAction func btnRightClick(_ sender: Any) { + getVcRoot()?.dismiss(animated: true) + //TODO: server + } + + func getVcRoot() -> VCRootCreateNew? { + return self.parent as? VCRootCreateNew + } +} + diff --git a/App/firstInput/VCInputId.swift b/App/firstInput/VCInputId.swift new file mode 100644 index 0000000..59295c1 --- /dev/null +++ b/App/firstInput/VCInputId.swift @@ -0,0 +1,80 @@ +import UIKit + +class VCInputId: UIViewController { + @IBOutlet weak var edtId: UITextField! + @IBOutlet weak var topMargin: NSLayoutConstraint! + + static func getInstance() -> VCInputId { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCInputId") as! VCInputId + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + registerKeyBoardEvent() + edtId.text = getVcRoot()?.id + } + + @IBAction func btnUnknowClick(_ sender: Any) { + } + + @IBAction func btnNextClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCSendTraoDoi.getInstance()) + } + + @IBAction func edtActionTrigerClick(_ sender: Any) { + edtId.endEditing(true) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + unregisterKeyBoardEvent() + } + + func getVcRoot() -> VCRoot? { + return self.parent as? VCRoot + } + + //region ======= keyboard process ==== + private func registerKeyBoardEvent() { + NotificationCenter.default.setObserver(self, selector: #selector(VCInputId.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow.rawValue, object: nil); + NotificationCenter.default.setObserver(self, selector: #selector(VCInputId.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide.rawValue, object: nil); + } + + private func unregisterKeyBoardEvent() { + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) + } + + func keyboardWillShow(_ notification: Notification) { + guard let userInfo = notification.userInfo else { + return + } + guard let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue else { + return + } + guard let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue else { + return + } + topMargin.constant = 50 - keyboardFrame.height + UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .curveLinear, animations: { () -> Void in + self.view.layoutIfNeeded() + }, completion: nil) + } + + func keyboardWillHide(_ notification: Notification) { + guard let userInfo = notification.userInfo else { + return + } + guard let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue else { + return + } + topMargin.constant = 50 + UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .curveLinear, animations: { () -> Void in + self.view.layoutIfNeeded() + }, completion: nil) + } + //endregion +} + diff --git a/App/firstInput/VCNoiDungTraoDoi.swift b/App/firstInput/VCNoiDungTraoDoi.swift new file mode 100644 index 0000000..82c9140 --- /dev/null +++ b/App/firstInput/VCNoiDungTraoDoi.swift @@ -0,0 +1,23 @@ +import UIKit + +class VCNoiDungTraoDoi: UIViewController { + + static func getInstance() -> VCNoiDungTraoDoi { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCNoiDungTraoDoi") as! VCNoiDungTraoDoi + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + } + + @IBAction func btnBackToTopClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCHome.getInstance()) + } + + func getVcRoot() -> VCRoot? { + return self.parent as? VCRoot + } +} + diff --git a/App/firstInput/VCSendTraoDoi.swift b/App/firstInput/VCSendTraoDoi.swift new file mode 100644 index 0000000..a294d0f --- /dev/null +++ b/App/firstInput/VCSendTraoDoi.swift @@ -0,0 +1,82 @@ +import UIKit + +class VCSendTraoDoi: UIViewController { + @IBOutlet weak var edtTenCty: UITextField! + @IBOutlet weak var edtPhone: UITextField! + @IBOutlet weak var edtAdress: UITextField! + @IBOutlet weak var edtContent: UITextViewCustom! + + static func getInstance() -> VCSendTraoDoi { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCSendTraoDoi") as! VCSendTraoDoi + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() +// registerKeyBoardEvent() + addInputAccessoryForTextFields([edtTenCty, edtPhone, edtAdress, edtContent], dismissable: true, previousNextable: true) + } + + @IBAction func btnCancelClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCInputId.getInstance()) + } + + @IBAction func btnSendClick(_ sender: Any) { + _ = getVcRoot()?.changeCurrentController(VCNoiDungTraoDoi.getInstance()) + } + +// @IBAction func edtActionTrigerClick(_ sender: Any) { +// } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) +// unregisterKeyBoardEvent() + } + + func getVcRoot() -> VCRoot? { + return self.parent as? VCRoot + } + + //region ======= keyboard process ==== + private func registerKeyBoardEvent() { + NotificationCenter.default.setObserver(self, selector: #selector(VCInputId.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow.rawValue, object: nil); + NotificationCenter.default.setObserver(self, selector: #selector(VCInputId.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide.rawValue, object: nil); + } + + private func unregisterKeyBoardEvent() { + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) + } + + func keyboardWillShow(_ notification: Notification) { +// guard let userInfo = notification.userInfo else { +// return +// } +// guard let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue else { +// return +// } +// guard let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue else { +// return +// } +// topMargin.constant = 50 - keyboardFrame.height +// UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .curveLinear, animations: { () -> Void in +// self.view.layoutIfNeeded() +// }, completion: nil) + } + + func keyboardWillHide(_ notification: Notification) { +// guard let userInfo = notification.userInfo else { +// return +// } +// guard let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue else { +// return +// } +// topMargin.constant = 50 +// UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .curveLinear, animations: { () -> Void in +// self.view.layoutIfNeeded() +// }, completion: nil) + } + //endregion +} + diff --git a/App/listCtruong/CellCtruong.swift b/App/listCtruong/CellCtruong.swift new file mode 100644 index 0000000..c904f94 --- /dev/null +++ b/App/listCtruong/CellCtruong.swift @@ -0,0 +1,9 @@ +import Foundation +import UIKit + +class CellCtruong: BaseTableViewCellUI { + + override func configCellWithData(baseObj: Any, index: Int) { + super.configCellWithData(baseObj: baseObj, index: index) + } +} diff --git a/App/listCtruong/VCListCtruong.swift b/App/listCtruong/VCListCtruong.swift new file mode 100644 index 0000000..6add19a --- /dev/null +++ b/App/listCtruong/VCListCtruong.swift @@ -0,0 +1,50 @@ +import UIKit + +class VCListCtruong: UIViewController, IVCLoadDataTableViewUIThread { + @IBOutlet weak var tableView: UITableViewLoadDataFromUIThread! + @IBOutlet weak var vTopLogo: VTopLogo! + + + static func getInstance() -> VCListCtruong { + let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + let vcOpen = storyboard.instantiateViewController(withIdentifier: "VCListCtruong") as! VCListCtruong + return vcOpen + } + + override func viewDidLoad() { + super.viewDidLoad() + self.tableView.separatorStyle = UITableViewCellSeparatorStyle.none + self.tableView.rowHeight = 95//UITableViewAutomaticDimension + self.tableView.estimatedRowHeight = 95 + tableView.initAndLoadData(self) + vTopLogo.leftBtnClick = { self.dismiss(animated: true) } + } + + func getVcRoot() -> VCRootListCtruong? { + return self.parent as? VCRootListCtruong + } + + //region TableView ======== + func loadDataOnUI(complete: @escaping ([Any]?) -> ()) { + var result = [CongTruong]() + result.append(CongTruong()) + result.append(CongTruong()) + result.append(CongTruong()) + complete(result) + } + + func getAllCell() -> [BaseCell] { + var baseCells: [BaseCell] = [BaseCell](); + baseCells.append(BaseCell(type: 0, identifier: "Cell")) + return baseCells + } + + func getTypeOfData(baseobj: Any) -> Int { + return 0 + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + } +//endregion +} + diff --git a/App/listCtruong/VCRootListCtruong.swift b/App/listCtruong/VCRootListCtruong.swift new file mode 100644 index 0000000..77f3120 --- /dev/null +++ b/App/listCtruong/VCRootListCtruong.swift @@ -0,0 +1,14 @@ +import UIKit +import Foundation + +class VCRootListCtruong: VCContainerFullScreen { + static func openController(_ viewController: UIViewController) { + let vcOpen = VCRootListCtruong() + viewController.present(vcOpen, animated: true, completion: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + changeCurrentController(VCListCtruong.getInstance()) + } +} diff --git a/App/model/CongTruong.swift b/App/model/CongTruong.swift new file mode 100644 index 0000000..a2e4e53 --- /dev/null +++ b/App/model/CongTruong.swift @@ -0,0 +1,12 @@ +// +// Created by Philip Tran on 2/25/17. +// + +import Foundation + +class CongTruong { + var tenCtuong: String? + var tenCty: String? + var daChup: Int?, conLai: Int? + var mucChups: [MucChup]? +} diff --git a/App/model/MucChup.swift b/App/model/MucChup.swift new file mode 100644 index 0000000..4d15738 --- /dev/null +++ b/App/model/MucChup.swift @@ -0,0 +1,23 @@ +// +// Created by Philip Tran on 2/25/17. +// + +import Foundation + +class MucChup: Equatable { + var id: Int? + var name: String? + var isSelected: Bool? + + init(id: Int? = nil, name: String? = nil) { + self.id = id + self.name = name + } +} + +func ==(lhs: MucChup, rhs: MucChup) -> Bool { + if let lhsName = lhs.name, let rhsName = rhs.name { + return lhsName == rhsName + } + return ObjectIdentifier(lhs) == ObjectIdentifier(rhs) +} diff --git a/App/model/Session.swift b/App/model/Session.swift new file mode 100644 index 0000000..9a28ae3 --- /dev/null +++ b/App/model/Session.swift @@ -0,0 +1,28 @@ + +import Foundation + +class Session{ + var type: SessionType? + var title: String? + var otherData: Any? + + init(title: String?) { + self.title = title + } + + init(type: SessionType, title: String?) { + self.type = type + self.title = title + } + + init(otherData: Any, title: String?) { + self.otherData = otherData + self.title = title + } +} + +enum SessionType{ + case LoadMore +} + + diff --git a/App/utils/SecurityUtils.swift b/App/utils/SecurityUtils.swift new file mode 100644 index 0000000..ae2eeb8 --- /dev/null +++ b/App/utils/SecurityUtils.swift @@ -0,0 +1,13 @@ + +import Foundation + +class SecurityUtils{ + + static func decryptData(data: Data) -> String?{ + return nil + } + + static func decryptData(data: Data) -> Data?{ + return nil + } +} diff --git a/Morooka.xcodeproj/project.pbxproj b/Morooka.xcodeproj/project.pbxproj new file mode 100644 index 0000000..faf8f07 --- /dev/null +++ b/Morooka.xcodeproj/project.pbxproj @@ -0,0 +1,1027 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 660BB033B71888619909D955 /* CellMucChupWithDelete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB4BD64E5830D866508ED /* CellMucChupWithDelete.swift */; }; + 660BB1419EB7F9A53F254A34 /* VCHome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB5CCC89015E195B8616B /* VCHome.swift */; }; + 660BB1B30E2061006033A438 /* VCNhapMucChup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB37070A33516AE4D7DE7 /* VCNhapMucChup.swift */; }; + 660BB1BFD392263F36E00D87 /* VCNhapTenCtruong.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB50191A22EBA51FE1A0C /* VCNhapTenCtruong.swift */; }; + 660BB1E9B67B393274F144F5 /* VCConfirmMucDaChon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBC94B22F8707D2D71D54 /* VCConfirmMucDaChon.swift */; }; + 660BB1EB08BCA748E7C80012 /* CongTruong.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB8C3076658F6777F23F4 /* CongTruong.swift */; }; + 660BB3513899304EDAAF2011 /* VCRootCreateNew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB6690D915951748165C5 /* VCRootCreateNew.swift */; }; + 660BB3D4A7491C38F390AAF5 /* VCInputId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBE8DACAA2464075AFFF8 /* VCInputId.swift */; }; + 660BB424710C3127B2550F70 /* CellCtruong.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBABAE50EFCA1391B5F12 /* CellCtruong.swift */; }; + 660BB48A6B73B6536343253E /* VCNoiDungTraoDoi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBC8827477D7EE87EA3F8 /* VCNoiDungTraoDoi.swift */; }; + 660BB5C735A56E6F6B18AEDB /* VTopLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB0E00FC88552ADCCED03 /* VTopLogo.swift */; }; + 660BB6A09C8708D39AFCBA42 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB4DD0B25B26182BAE9F2 /* Session.swift */; }; + 660BB6E850E4A6535EB9490E /* VCSettingBienQC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBA9A96B161EC470D3B1D /* VCSettingBienQC.swift */; }; + 660BB78D49C4DA59425EADC9 /* VCSendTraoDoi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBCA62C72AB86E38044EC /* VCSendTraoDoi.swift */; }; + 660BB7D60756B330D34A2882 /* VCListCtruong.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBB53637040BA5B94E85D /* VCListCtruong.swift */; }; + 660BB83E7195A6132D8B6406 /* VCRootListCtruong.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB9D5D76B5C71B10B5BD2 /* VCRootListCtruong.swift */; }; + 660BB91CDF86A3CB452EAED6 /* SecurityUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB7E1FEE6358AA0FF7932 /* SecurityUtils.swift */; }; + 660BBA1DAD88079D5AFF4E9D /* VCRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB702D96360C446B9F0F8 /* VCRoot.swift */; }; + 660BBCD1A02935C5843544B6 /* MucChup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB6D0167C62DED01BDA8D /* MucChup.swift */; }; + 660BBD73E0E1916B0402099D /* VCChonMucChup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB200DE1903184FAFF325 /* VCChonMucChup.swift */; }; + 660BBD91D7ED633F0D6C8F82 /* VCNhapTenCty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBEDF8CDE53E1B271BD54 /* VCNhapTenCty.swift */; }; + 660BBDAFF0683FBC3164980E /* UITextViewCustom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBE7B9A67082CF3CF2ED1 /* UITextViewCustom.swift */; }; + 660BBDD680ACA84807E94D2A /* VCContainerFullScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBA90FAB43F3681BAE0EC /* VCContainerFullScreen.swift */; }; + 660BBF6EB7BCBC3D7B293365 /* CellMucChupWithCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BBDE624BF1003153C83B9 /* CellMucChupWithCamera.swift */; }; + 660BBFDEADB8218026818F26 /* CellChonMucChup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660BB07FA9FE3DE9908DD8E9 /* CellChonMucChup.swift */; }; + F9D8E6941E617E2E00D789BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9D8E6931E617E2E00D789BF /* Localizable.strings */; }; + F9F648141E6105EA009CCEBD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648131E6105EA009CCEBD /* AppDelegate.swift */; }; + F9F648191E6105EA009CCEBD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F9F648171E6105EA009CCEBD /* Main.storyboard */; }; + F9F6481B1E6105EA009CCEBD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F9F6481A1E6105EA009CCEBD /* Assets.xcassets */; }; + F9F6482A1E610632009CCEBD /* SplashController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648271E610632009CCEBD /* SplashController.swift */; }; + F9F648A21E6106E6009CCEBD /* AudioItemEventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648371E6106E6009CCEBD /* AudioItemEventProducer.swift */; }; + F9F648A31E6106E6009CCEBD /* EventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648381E6106E6009CCEBD /* EventProducer.swift */; }; + F9F648A41E6106E6009CCEBD /* NetworkEventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648391E6106E6009CCEBD /* NetworkEventProducer.swift */; }; + F9F648A51E6106E6009CCEBD /* PlayerEventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6483A1E6106E6009CCEBD /* PlayerEventProducer.swift */; }; + F9F648A61E6106E6009CCEBD /* QualityAdjustmentEventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6483B1E6106E6009CCEBD /* QualityAdjustmentEventProducer.swift */; }; + F9F648A71E6106E6009CCEBD /* RetryEventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6483C1E6106E6009CCEBD /* RetryEventProducer.swift */; }; + F9F648A81E6106E6009CCEBD /* SeekEventProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6483D1E6106E6009CCEBD /* SeekEventProducer.swift */; }; + F9F648A91E6106E6009CCEBD /* AudioItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6483F1E6106E6009CCEBD /* AudioItem.swift */; }; + F9F648AA1E6106E6009CCEBD /* AudioItemQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648401E6106E6009CCEBD /* AudioItemQueue.swift */; }; + F9F648AB1E6106E6009CCEBD /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648421E6106E6009CCEBD /* AudioPlayer.swift */; }; + F9F648AC1E6106E6009CCEBD /* AudioPlayerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648431E6106E6009CCEBD /* AudioPlayerDelegate.swift */; }; + F9F648AD1E6106E6009CCEBD /* AudioPlayerMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648441E6106E6009CCEBD /* AudioPlayerMode.swift */; }; + F9F648AE1E6106E6009CCEBD /* AudioPlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648451E6106E6009CCEBD /* AudioPlayerState.swift */; }; + F9F648AF1E6106E6009CCEBD /* AudioPlayer+AudioItemEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648471E6106E6009CCEBD /* AudioPlayer+AudioItemEvent.swift */; }; + F9F648B01E6106E6009CCEBD /* AudioPlayer+Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648481E6106E6009CCEBD /* AudioPlayer+Control.swift */; }; + F9F648B11E6106E6009CCEBD /* AudioPlayer+CurrentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648491E6106E6009CCEBD /* AudioPlayer+CurrentItem.swift */; }; + F9F648B21E6106E6009CCEBD /* AudioPlayer+NetworkEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6484A1E6106E6009CCEBD /* AudioPlayer+NetworkEvent.swift */; }; + F9F648B31E6106E6009CCEBD /* AudioPlayer+PlayerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6484B1E6106E6009CCEBD /* AudioPlayer+PlayerEvent.swift */; }; + F9F648B41E6106E6009CCEBD /* AudioPlayer+QualityAdjustmentEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6484C1E6106E6009CCEBD /* AudioPlayer+QualityAdjustmentEvent.swift */; }; + F9F648B51E6106E6009CCEBD /* AudioPlayer+Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6484D1E6106E6009CCEBD /* AudioPlayer+Queue.swift */; }; + F9F648B61E6106E6009CCEBD /* AudioPlayer+RetryEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6484E1E6106E6009CCEBD /* AudioPlayer+RetryEvent.swift */; }; + F9F648B71E6106E6009CCEBD /* AudioPlayer+SeekEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6484F1E6106E6009CCEBD /* AudioPlayer+SeekEvent.swift */; }; + F9F648B81E6106E6009CCEBD /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648501E6106E6009CCEBD /* Reachability.swift */; }; + F9F648B91E6106E6009CCEBD /* BackgroundHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648521E6106E6009CCEBD /* BackgroundHandler.swift */; }; + F9F648BA1E6106E6009CCEBD /* CMTime+TimeIntervalValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648531E6106E6009CCEBD /* CMTime+TimeIntervalValue.swift */; }; + F9F648BB1E6106E6009CCEBD /* MPNowPlayingInfoCenter+AudioItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648541E6106E6009CCEBD /* MPNowPlayingInfoCenter+AudioItem.swift */; }; + F9F648BC1E6106E6009CCEBD /* URL+Offline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648551E6106E6009CCEBD /* URL+Offline.swift */; }; + F9F648BD1E6106E6009CCEBD /* MediaPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648561E6106E6009CCEBD /* MediaPlayer.swift */; }; + F9F648BE1E6106E6009CCEBD /* CacheUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648571E6106E6009CCEBD /* CacheUtils.swift */; }; + F9F648BF1E6106E6009CCEBD /* CommonExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648581E6106E6009CCEBD /* CommonExtension.swift */; }; + F9F648C01E6106E6009CCEBD /* CommonUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648591E6106E6009CCEBD /* CommonUtils.swift */; }; + F9F648C11E6106E6009CCEBD /* DataTypeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6485A1E6106E6009CCEBD /* DataTypeUtils.swift */; }; + F9F648C21E6106E6009CCEBD /* DialogUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6485B1E6106E6009CCEBD /* DialogUtils.swift */; }; + F9F648C31E6106E6009CCEBD /* DownloadTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6485C1E6106E6009CCEBD /* DownloadTask.swift */; }; + F9F648C41E6106E6009CCEBD /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6485D1E6106E6009CCEBD /* FileUtils.swift */; }; + F9F648C51E6106E6009CCEBD /* NetWorkUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6485E1E6106E6009CCEBD /* NetWorkUtils.swift */; }; + F9F648C61E6106E6009CCEBD /* PreferenceUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6485F1E6106E6009CCEBD /* PreferenceUtils.swift */; }; + F9F648C71E6106E6009CCEBD /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648601E6106E6009CCEBD /* Queue.swift */; }; + F9F648C81E6106E6009CCEBD /* RecordUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648611E6106E6009CCEBD /* RecordUtils.swift */; }; + F9F648C91E6106E6009CCEBD /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648621E6106E6009CCEBD /* StringExtension.swift */; }; + F9F648CA1E6106E6009CCEBD /* SwiftOverlays.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648631E6106E6009CCEBD /* SwiftOverlays.swift */; }; + F9F648CC1E6106E6009CCEBD /* Jukebox.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648671E6106E6009CCEBD /* Jukebox.swift */; }; + F9F648CD1E6106E6009CCEBD /* JukeboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648681E6106E6009CCEBD /* JukeboxItem.swift */; }; + F9F648CE1E6106E6009CCEBD /* AnimatedMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6486B1E6106E6009CCEBD /* AnimatedMenuButton.swift */; }; + F9F648CF1E6106E6009CCEBD /* DrawerBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6486C1E6106E6009CCEBD /* DrawerBarButtonItem.swift */; }; + F9F648D01E6106E6009CCEBD /* DrawerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6486E1E6106E6009CCEBD /* DrawerController.swift */; }; + F9F648D11E6106E6009CCEBD /* DrawerVisualState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6486F1E6106E6009CCEBD /* DrawerVisualState.swift */; }; + F9F648D21E6106E6009CCEBD /* DrawerVisualStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648701E6106E6009CCEBD /* DrawerVisualStateManager.swift */; }; + F9F648D41E6106E6009CCEBD /* IContainerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648721E6106E6009CCEBD /* IContainerController.swift */; }; + F9F648D51E6106E6009CCEBD /* AnimatedControllerFromBottom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648741E6106E6009CCEBD /* AnimatedControllerFromBottom.swift */; }; + F9F648D61E6106E6009CCEBD /* AnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648751E6106E6009CCEBD /* AnimatedTransitioning.swift */; }; + F9F648D71E6106E6009CCEBD /* BaseUIPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648761E6106E6009CCEBD /* BaseUIPresentationController.swift */; }; + F9F648D81E6106E6009CCEBD /* BaseViewControllerShowTransculent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648771E6106E6009CCEBD /* BaseViewControllerShowTransculent.swift */; }; + F9F648D91E6106E6009CCEBD /* VCContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648781E6106E6009CCEBD /* VCContainer.swift */; }; + F9F648DA1E6106E6009CCEBD /* UICollectionViewLoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6487B1E6106E6009CCEBD /* UICollectionViewLoadData.swift */; }; + F9F648DB1E6106E6009CCEBD /* UICollectionViewLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6487C1E6106E6009CCEBD /* UICollectionViewLoading.swift */; }; + F9F648DC1E6106E6009CCEBD /* ComboBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6487E1E6106E6009CCEBD /* ComboBox.swift */; }; + F9F648DD1E6106E6009CCEBD /* DPDConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6487F1E6106E6009CCEBD /* DPDConstants.swift */; }; + F9F648DE1E6106E6009CCEBD /* DPDKeyboardListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648801E6106E6009CCEBD /* DPDKeyboardListener.swift */; }; + F9F648DF1E6106E6009CCEBD /* DPDUIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648811E6106E6009CCEBD /* DPDUIView+Extension.swift */; }; + F9F648E01E6106E6009CCEBD /* DropDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648821E6106E6009CCEBD /* DropDown.swift */; }; + F9F648E11E6106E6009CCEBD /* DropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648831E6106E6009CCEBD /* DropDownCell.swift */; }; + F9F648E21E6106E6009CCEBD /* FloatRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648841E6106E6009CCEBD /* FloatRatingView.swift */; }; + F9F648E31E6106E6009CCEBD /* ISRadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648851E6106E6009CCEBD /* ISRadioButton.swift */; }; + F9F648E41E6106E6009CCEBD /* BaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648871E6106E6009CCEBD /* BaseCell.swift */; }; + F9F648E51E6106E6009CCEBD /* BaseTbLoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648881E6106E6009CCEBD /* BaseTbLoadData.swift */; }; + F9F648E61E6106E6009CCEBD /* CellLoadMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648891E6106E6009CCEBD /* CellLoadMore.swift */; }; + F9F648E71E6106E6009CCEBD /* UITableViewLoadDataBg.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6488A1E6106E6009CCEBD /* UITableViewLoadDataBg.swift */; }; + F9F648E81E6106E6009CCEBD /* UITableViewLoadDataFromUIThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6488B1E6106E6009CCEBD /* UITableViewLoadDataFromUIThread.swift */; }; + F9F648E91E6106E6009CCEBD /* UITableViewLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6488C1E6106E6009CCEBD /* UITableViewLoading.swift */; }; + F9F648EA1E6106E6009CCEBD /* UITableViewLoadMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6488D1E6106E6009CCEBD /* UITableViewLoadMore.swift */; }; + F9F648EB1E6106E6009CCEBD /* UIButtonCustom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6488E1E6106E6009CCEBD /* UIButtonCustom.swift */; }; + F9F648EC1E6106E6009CCEBD /* UIImageViewCustom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6488F1E6106E6009CCEBD /* UIImageViewCustom.swift */; }; + F9F648ED1E6106E6009CCEBD /* UILabelCustom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648901E6106E6009CCEBD /* UILabelCustom.swift */; }; + F9F648EE1E6106E6009CCEBD /* UILableCustomLeftImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648911E6106E6009CCEBD /* UILableCustomLeftImage.swift */; }; + F9F648EF1E6106E6009CCEBD /* UIViewCustom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648921E6106E6009CCEBD /* UIViewCustom.swift */; }; + F9F648F01E6106E6009CCEBD /* UIViewCustomGradientBg.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648931E6106E6009CCEBD /* UIViewCustomGradientBg.swift */; }; + F9F648F11E6106E6009CCEBD /* UIViewCustomPressHightLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648941E6106E6009CCEBD /* UIViewCustomPressHightLight.swift */; }; + F9F648F21E6106E6009CCEBD /* VPlayerControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648951E6106E6009CCEBD /* VPlayerControl.swift */; }; + F9F648F31E6106E6009CCEBD /* ViewTopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F648971E6106E6009CCEBD /* ViewTopBar.swift */; }; + F9F648F41E6106E6009CCEBD /* ViewTopBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9F648981E6106E6009CCEBD /* ViewTopBar.xib */; }; + F9F648F51E6106E6009CCEBD /* VTopNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F6489A1E6106E6009CCEBD /* VTopNotification.swift */; }; + F9F648F61E6106E6009CCEBD /* VTopNotification.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9F6489B1E6106E6009CCEBD /* VTopNotification.xib */; }; + F9F648F81E6123B6009CCEBD /* VTopLogo.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9F648F71E6123B6009CCEBD /* VTopLogo.xib */; }; + FBCBF18D5121047C2463F104 /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 400AC4177173A04F0AF1EC54 /* Pods_App.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 400AC4177173A04F0AF1EC54 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 660BB07FA9FE3DE9908DD8E9 /* CellChonMucChup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellChonMucChup.swift; sourceTree = ""; }; + 660BB0E00FC88552ADCCED03 /* VTopLogo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VTopLogo.swift; sourceTree = ""; }; + 660BB200DE1903184FAFF325 /* VCChonMucChup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCChonMucChup.swift; sourceTree = ""; }; + 660BB37070A33516AE4D7DE7 /* VCNhapMucChup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCNhapMucChup.swift; sourceTree = ""; }; + 660BB4BD64E5830D866508ED /* CellMucChupWithDelete.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellMucChupWithDelete.swift; sourceTree = ""; }; + 660BB4DD0B25B26182BAE9F2 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; + 660BB50191A22EBA51FE1A0C /* VCNhapTenCtruong.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCNhapTenCtruong.swift; sourceTree = ""; }; + 660BB5CCC89015E195B8616B /* VCHome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCHome.swift; sourceTree = ""; }; + 660BB6690D915951748165C5 /* VCRootCreateNew.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCRootCreateNew.swift; sourceTree = ""; }; + 660BB6D0167C62DED01BDA8D /* MucChup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MucChup.swift; sourceTree = ""; }; + 660BB702D96360C446B9F0F8 /* VCRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCRoot.swift; sourceTree = ""; }; + 660BB7E1FEE6358AA0FF7932 /* SecurityUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurityUtils.swift; sourceTree = ""; }; + 660BB8C3076658F6777F23F4 /* CongTruong.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CongTruong.swift; sourceTree = ""; }; + 660BB9D5D76B5C71B10B5BD2 /* VCRootListCtruong.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCRootListCtruong.swift; sourceTree = ""; }; + 660BBA90FAB43F3681BAE0EC /* VCContainerFullScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCContainerFullScreen.swift; sourceTree = ""; }; + 660BBA9A96B161EC470D3B1D /* VCSettingBienQC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCSettingBienQC.swift; sourceTree = ""; }; + 660BBABAE50EFCA1391B5F12 /* CellCtruong.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellCtruong.swift; sourceTree = ""; }; + 660BBB53637040BA5B94E85D /* VCListCtruong.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCListCtruong.swift; sourceTree = ""; }; + 660BBC8827477D7EE87EA3F8 /* VCNoiDungTraoDoi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCNoiDungTraoDoi.swift; sourceTree = ""; }; + 660BBC94B22F8707D2D71D54 /* VCConfirmMucDaChon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCConfirmMucDaChon.swift; sourceTree = ""; }; + 660BBCA62C72AB86E38044EC /* VCSendTraoDoi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCSendTraoDoi.swift; sourceTree = ""; }; + 660BBDE624BF1003153C83B9 /* CellMucChupWithCamera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellMucChupWithCamera.swift; sourceTree = ""; }; + 660BBE7B9A67082CF3CF2ED1 /* UITextViewCustom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewCustom.swift; sourceTree = ""; }; + 660BBE8DACAA2464075AFFF8 /* VCInputId.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCInputId.swift; sourceTree = ""; }; + 660BBEDF8CDE53E1B271BD54 /* VCNhapTenCty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCNhapTenCty.swift; sourceTree = ""; }; + 81CE8FEDDF2FC5FC5C1AD6CB /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = ""; }; + BEBE6DCE6BD561EC73CCA219 /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = ""; }; + F9D8E6931E617E2E00D789BF /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + F9F648101E6105EA009CCEBD /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F9F648131E6105EA009CCEBD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + F9F648181E6105EA009CCEBD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + F9F6481A1E6105EA009CCEBD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + F9F6481F1E6105EA009CCEBD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F9F648271E610632009CCEBD /* SplashController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashController.swift; sourceTree = ""; }; + F9F648371E6106E6009CCEBD /* AudioItemEventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioItemEventProducer.swift; sourceTree = ""; }; + F9F648381E6106E6009CCEBD /* EventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventProducer.swift; sourceTree = ""; }; + F9F648391E6106E6009CCEBD /* NetworkEventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkEventProducer.swift; sourceTree = ""; }; + F9F6483A1E6106E6009CCEBD /* PlayerEventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerEventProducer.swift; sourceTree = ""; }; + F9F6483B1E6106E6009CCEBD /* QualityAdjustmentEventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QualityAdjustmentEventProducer.swift; sourceTree = ""; }; + F9F6483C1E6106E6009CCEBD /* RetryEventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryEventProducer.swift; sourceTree = ""; }; + F9F6483D1E6106E6009CCEBD /* SeekEventProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeekEventProducer.swift; sourceTree = ""; }; + F9F6483F1E6106E6009CCEBD /* AudioItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioItem.swift; sourceTree = ""; }; + F9F648401E6106E6009CCEBD /* AudioItemQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioItemQueue.swift; sourceTree = ""; }; + F9F648421E6106E6009CCEBD /* AudioPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; + F9F648431E6106E6009CCEBD /* AudioPlayerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayerDelegate.swift; sourceTree = ""; }; + F9F648441E6106E6009CCEBD /* AudioPlayerMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayerMode.swift; sourceTree = ""; }; + F9F648451E6106E6009CCEBD /* AudioPlayerState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = ""; }; + F9F648471E6106E6009CCEBD /* AudioPlayer+AudioItemEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+AudioItemEvent.swift"; sourceTree = ""; }; + F9F648481E6106E6009CCEBD /* AudioPlayer+Control.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+Control.swift"; sourceTree = ""; }; + F9F648491E6106E6009CCEBD /* AudioPlayer+CurrentItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+CurrentItem.swift"; sourceTree = ""; }; + F9F6484A1E6106E6009CCEBD /* AudioPlayer+NetworkEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+NetworkEvent.swift"; sourceTree = ""; }; + F9F6484B1E6106E6009CCEBD /* AudioPlayer+PlayerEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+PlayerEvent.swift"; sourceTree = ""; }; + F9F6484C1E6106E6009CCEBD /* AudioPlayer+QualityAdjustmentEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+QualityAdjustmentEvent.swift"; sourceTree = ""; }; + F9F6484D1E6106E6009CCEBD /* AudioPlayer+Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+Queue.swift"; sourceTree = ""; }; + F9F6484E1E6106E6009CCEBD /* AudioPlayer+RetryEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+RetryEvent.swift"; sourceTree = ""; }; + F9F6484F1E6106E6009CCEBD /* AudioPlayer+SeekEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AudioPlayer+SeekEvent.swift"; sourceTree = ""; }; + F9F648501E6106E6009CCEBD /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; + F9F648521E6106E6009CCEBD /* BackgroundHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundHandler.swift; sourceTree = ""; }; + F9F648531E6106E6009CCEBD /* CMTime+TimeIntervalValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CMTime+TimeIntervalValue.swift"; sourceTree = ""; }; + F9F648541E6106E6009CCEBD /* MPNowPlayingInfoCenter+AudioItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MPNowPlayingInfoCenter+AudioItem.swift"; sourceTree = ""; }; + F9F648551E6106E6009CCEBD /* URL+Offline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Offline.swift"; sourceTree = ""; }; + F9F648561E6106E6009CCEBD /* MediaPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPlayer.swift; sourceTree = ""; }; + F9F648571E6106E6009CCEBD /* CacheUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheUtils.swift; sourceTree = ""; }; + F9F648581E6106E6009CCEBD /* CommonExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonExtension.swift; sourceTree = ""; }; + F9F648591E6106E6009CCEBD /* CommonUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonUtils.swift; sourceTree = ""; }; + F9F6485A1E6106E6009CCEBD /* DataTypeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataTypeUtils.swift; sourceTree = ""; }; + F9F6485B1E6106E6009CCEBD /* DialogUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialogUtils.swift; sourceTree = ""; }; + F9F6485C1E6106E6009CCEBD /* DownloadTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadTask.swift; sourceTree = ""; }; + F9F6485D1E6106E6009CCEBD /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; + F9F6485E1E6106E6009CCEBD /* NetWorkUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetWorkUtils.swift; sourceTree = ""; }; + F9F6485F1E6106E6009CCEBD /* PreferenceUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferenceUtils.swift; sourceTree = ""; }; + F9F648601E6106E6009CCEBD /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; + F9F648611E6106E6009CCEBD /* RecordUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUtils.swift; sourceTree = ""; }; + F9F648621E6106E6009CCEBD /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; + F9F648631E6106E6009CCEBD /* SwiftOverlays.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftOverlays.swift; sourceTree = ""; }; + F9F648671E6106E6009CCEBD /* Jukebox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Jukebox.swift; sourceTree = ""; }; + F9F648681E6106E6009CCEBD /* JukeboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JukeboxItem.swift; sourceTree = ""; }; + F9F6486B1E6106E6009CCEBD /* AnimatedMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedMenuButton.swift; sourceTree = ""; }; + F9F6486C1E6106E6009CCEBD /* DrawerBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerBarButtonItem.swift; sourceTree = ""; }; + F9F6486D1E6106E6009CCEBD /* DrawerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawerController.h; sourceTree = ""; }; + F9F6486E1E6106E6009CCEBD /* DrawerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerController.swift; sourceTree = ""; }; + F9F6486F1E6106E6009CCEBD /* DrawerVisualState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerVisualState.swift; sourceTree = ""; }; + F9F648701E6106E6009CCEBD /* DrawerVisualStateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerVisualStateManager.swift; sourceTree = ""; }; + F9F648721E6106E6009CCEBD /* IContainerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IContainerController.swift; sourceTree = ""; }; + F9F648741E6106E6009CCEBD /* AnimatedControllerFromBottom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedControllerFromBottom.swift; sourceTree = ""; }; + F9F648751E6106E6009CCEBD /* AnimatedTransitioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedTransitioning.swift; sourceTree = ""; }; + F9F648761E6106E6009CCEBD /* BaseUIPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUIPresentationController.swift; sourceTree = ""; }; + F9F648771E6106E6009CCEBD /* BaseViewControllerShowTransculent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewControllerShowTransculent.swift; sourceTree = ""; }; + F9F648781E6106E6009CCEBD /* VCContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCContainer.swift; sourceTree = ""; }; + F9F6487B1E6106E6009CCEBD /* UICollectionViewLoadData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewLoadData.swift; sourceTree = ""; }; + F9F6487C1E6106E6009CCEBD /* UICollectionViewLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewLoading.swift; sourceTree = ""; }; + F9F6487E1E6106E6009CCEBD /* ComboBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComboBox.swift; sourceTree = ""; }; + F9F6487F1E6106E6009CCEBD /* DPDConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPDConstants.swift; sourceTree = ""; }; + F9F648801E6106E6009CCEBD /* DPDKeyboardListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPDKeyboardListener.swift; sourceTree = ""; }; + F9F648811E6106E6009CCEBD /* DPDUIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DPDUIView+Extension.swift"; sourceTree = ""; }; + F9F648821E6106E6009CCEBD /* DropDown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDown.swift; sourceTree = ""; }; + F9F648831E6106E6009CCEBD /* DropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownCell.swift; sourceTree = ""; }; + F9F648841E6106E6009CCEBD /* FloatRatingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatRatingView.swift; sourceTree = ""; }; + F9F648851E6106E6009CCEBD /* ISRadioButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISRadioButton.swift; sourceTree = ""; }; + F9F648871E6106E6009CCEBD /* BaseCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCell.swift; sourceTree = ""; }; + F9F648881E6106E6009CCEBD /* BaseTbLoadData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTbLoadData.swift; sourceTree = ""; }; + F9F648891E6106E6009CCEBD /* CellLoadMore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellLoadMore.swift; sourceTree = ""; }; + F9F6488A1E6106E6009CCEBD /* UITableViewLoadDataBg.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewLoadDataBg.swift; sourceTree = ""; }; + F9F6488B1E6106E6009CCEBD /* UITableViewLoadDataFromUIThread.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewLoadDataFromUIThread.swift; sourceTree = ""; }; + F9F6488C1E6106E6009CCEBD /* UITableViewLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewLoading.swift; sourceTree = ""; }; + F9F6488D1E6106E6009CCEBD /* UITableViewLoadMore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewLoadMore.swift; sourceTree = ""; }; + F9F6488E1E6106E6009CCEBD /* UIButtonCustom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonCustom.swift; sourceTree = ""; }; + F9F6488F1E6106E6009CCEBD /* UIImageViewCustom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewCustom.swift; sourceTree = ""; }; + F9F648901E6106E6009CCEBD /* UILabelCustom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelCustom.swift; sourceTree = ""; }; + F9F648911E6106E6009CCEBD /* UILableCustomLeftImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILableCustomLeftImage.swift; sourceTree = ""; }; + F9F648921E6106E6009CCEBD /* UIViewCustom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewCustom.swift; sourceTree = ""; }; + F9F648931E6106E6009CCEBD /* UIViewCustomGradientBg.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewCustomGradientBg.swift; sourceTree = ""; }; + F9F648941E6106E6009CCEBD /* UIViewCustomPressHightLight.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewCustomPressHightLight.swift; sourceTree = ""; }; + F9F648951E6106E6009CCEBD /* VPlayerControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPlayerControl.swift; sourceTree = ""; }; + F9F648971E6106E6009CCEBD /* ViewTopBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewTopBar.swift; sourceTree = ""; }; + F9F648981E6106E6009CCEBD /* ViewTopBar.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ViewTopBar.xib; sourceTree = ""; }; + F9F6489A1E6106E6009CCEBD /* VTopNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VTopNotification.swift; sourceTree = ""; }; + F9F6489B1E6106E6009CCEBD /* VTopNotification.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VTopNotification.xib; sourceTree = ""; }; + F9F648F71E6123B6009CCEBD /* VTopLogo.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VTopLogo.xib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F9F6480D1E6105EA009CCEBD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FBCBF18D5121047C2463F104 /* Pods_App.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 660BB6CB16C4EBE73F81CEC3 /* commons */ = { + isa = PBXGroup; + children = ( + 660BB0E00FC88552ADCCED03 /* VTopLogo.swift */, + F9F648F71E6123B6009CCEBD /* VTopLogo.xib */, + 660BB702D96360C446B9F0F8 /* VCRoot.swift */, + 660BB5CCC89015E195B8616B /* VCHome.swift */, + 660BBA90FAB43F3681BAE0EC /* VCContainerFullScreen.swift */, + ); + path = commons; + sourceTree = ""; + }; + 660BB83093461D38454E827A /* model */ = { + isa = PBXGroup; + children = ( + 660BB4DD0B25B26182BAE9F2 /* Session.swift */, + 660BB6D0167C62DED01BDA8D /* MucChup.swift */, + 660BB8C3076658F6777F23F4 /* CongTruong.swift */, + ); + path = model; + sourceTree = ""; + }; + 660BB84D568EA6CBE019EA9F /* listCtruong */ = { + isa = PBXGroup; + children = ( + 660BBB53637040BA5B94E85D /* VCListCtruong.swift */, + 660BB9D5D76B5C71B10B5BD2 /* VCRootListCtruong.swift */, + 660BBABAE50EFCA1391B5F12 /* CellCtruong.swift */, + ); + path = listCtruong; + sourceTree = ""; + }; + 660BBA945CD13F48309CC81E /* createNew */ = { + isa = PBXGroup; + children = ( + 660BB6690D915951748165C5 /* VCRootCreateNew.swift */, + 660BBEDF8CDE53E1B271BD54 /* VCNhapTenCty.swift */, + 660BB50191A22EBA51FE1A0C /* VCNhapTenCtruong.swift */, + 660BB37070A33516AE4D7DE7 /* VCNhapMucChup.swift */, + 660BB4BD64E5830D866508ED /* CellMucChupWithDelete.swift */, + 660BB200DE1903184FAFF325 /* VCChonMucChup.swift */, + 660BB07FA9FE3DE9908DD8E9 /* CellChonMucChup.swift */, + 660BBC94B22F8707D2D71D54 /* VCConfirmMucDaChon.swift */, + 660BBDE624BF1003153C83B9 /* CellMucChupWithCamera.swift */, + 660BBA9A96B161EC470D3B1D /* VCSettingBienQC.swift */, + ); + path = createNew; + sourceTree = ""; + }; + 660BBCEAB5F9F374E58F3C19 /* utils */ = { + isa = PBXGroup; + children = ( + 660BB7E1FEE6358AA0FF7932 /* SecurityUtils.swift */, + ); + path = utils; + sourceTree = ""; + }; + 660BBD84EEF67E397C975BA4 /* firstInput */ = { + isa = PBXGroup; + children = ( + 660BBE8DACAA2464075AFFF8 /* VCInputId.swift */, + 660BBCA62C72AB86E38044EC /* VCSendTraoDoi.swift */, + 660BBC8827477D7EE87EA3F8 /* VCNoiDungTraoDoi.swift */, + ); + path = firstInput; + sourceTree = ""; + }; + 66FB84A4942A76C5AC467944 /* Pods */ = { + isa = PBXGroup; + children = ( + BEBE6DCE6BD561EC73CCA219 /* Pods-App.debug.xcconfig */, + 81CE8FEDDF2FC5FC5C1AD6CB /* Pods-App.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + F44D129BC6EF626BD41DF3BC /* Frameworks */ = { + isa = PBXGroup; + children = ( + 400AC4177173A04F0AF1EC54 /* Pods_App.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F9F648071E6105EA009CCEBD = { + isa = PBXGroup; + children = ( + F9F648121E6105EA009CCEBD /* App */, + F9F648111E6105EA009CCEBD /* Products */, + 66FB84A4942A76C5AC467944 /* Pods */, + F44D129BC6EF626BD41DF3BC /* Frameworks */, + ); + sourceTree = ""; + }; + F9F648111E6105EA009CCEBD /* Products */ = { + isa = PBXGroup; + children = ( + F9F648101E6105EA009CCEBD /* App.app */, + ); + name = Products; + sourceTree = ""; + }; + F9F648121E6105EA009CCEBD /* App */ = { + isa = PBXGroup; + children = ( + F9F648271E610632009CCEBD /* SplashController.swift */, + F9F648131E6105EA009CCEBD /* AppDelegate.swift */, + F9F648171E6105EA009CCEBD /* Main.storyboard */, + F9F6481A1E6105EA009CCEBD /* Assets.xcassets */, + F9F6481F1E6105EA009CCEBD /* Info.plist */, + F9F6482B1E6106E6009CCEBD /* GeneralUtilsFinal */, + 660BB83093461D38454E827A /* model */, + 660BBCEAB5F9F374E58F3C19 /* utils */, + 660BBD84EEF67E397C975BA4 /* firstInput */, + 660BB6CB16C4EBE73F81CEC3 /* commons */, + 660BBA945CD13F48309CC81E /* createNew */, + F9D8E6931E617E2E00D789BF /* Localizable.strings */, + 660BB84D568EA6CBE019EA9F /* listCtruong */, + ); + path = App; + sourceTree = ""; + }; + F9F6482B1E6106E6009CCEBD /* GeneralUtilsFinal */ = { + isa = PBXGroup; + children = ( + F9F648331E6106E6009CCEBD /* commonUtils */, + F9F648651E6106E6009CCEBD /* libs */, + F9F648691E6106E6009CCEBD /* ui */, + ); + name = GeneralUtilsFinal; + path = ../../../GeneralUtilsFinal; + sourceTree = ""; + }; + F9F648331E6106E6009CCEBD /* commonUtils */ = { + isa = PBXGroup; + children = ( + F9F648341E6106E6009CCEBD /* audio */, + F9F648571E6106E6009CCEBD /* CacheUtils.swift */, + F9F648581E6106E6009CCEBD /* CommonExtension.swift */, + F9F648591E6106E6009CCEBD /* CommonUtils.swift */, + F9F6485A1E6106E6009CCEBD /* DataTypeUtils.swift */, + F9F6485B1E6106E6009CCEBD /* DialogUtils.swift */, + F9F6485C1E6106E6009CCEBD /* DownloadTask.swift */, + F9F6485D1E6106E6009CCEBD /* FileUtils.swift */, + F9F6485E1E6106E6009CCEBD /* NetWorkUtils.swift */, + F9F6485F1E6106E6009CCEBD /* PreferenceUtils.swift */, + F9F648601E6106E6009CCEBD /* Queue.swift */, + F9F648611E6106E6009CCEBD /* RecordUtils.swift */, + F9F648621E6106E6009CCEBD /* StringExtension.swift */, + F9F648631E6106E6009CCEBD /* SwiftOverlays.swift */, + ); + path = commonUtils; + sourceTree = ""; + }; + F9F648341E6106E6009CCEBD /* audio */ = { + isa = PBXGroup; + children = ( + F9F648351E6106E6009CCEBD /* AudioPlayer */, + F9F648561E6106E6009CCEBD /* MediaPlayer.swift */, + ); + path = audio; + sourceTree = ""; + }; + F9F648351E6106E6009CCEBD /* AudioPlayer */ = { + isa = PBXGroup; + children = ( + F9F648361E6106E6009CCEBD /* event */, + F9F6483E1E6106E6009CCEBD /* item */, + F9F648411E6106E6009CCEBD /* player */, + F9F648501E6106E6009CCEBD /* Reachability.swift */, + F9F648511E6106E6009CCEBD /* utils */, + ); + path = AudioPlayer; + sourceTree = ""; + }; + F9F648361E6106E6009CCEBD /* event */ = { + isa = PBXGroup; + children = ( + F9F648371E6106E6009CCEBD /* AudioItemEventProducer.swift */, + F9F648381E6106E6009CCEBD /* EventProducer.swift */, + F9F648391E6106E6009CCEBD /* NetworkEventProducer.swift */, + F9F6483A1E6106E6009CCEBD /* PlayerEventProducer.swift */, + F9F6483B1E6106E6009CCEBD /* QualityAdjustmentEventProducer.swift */, + F9F6483C1E6106E6009CCEBD /* RetryEventProducer.swift */, + F9F6483D1E6106E6009CCEBD /* SeekEventProducer.swift */, + ); + path = event; + sourceTree = ""; + }; + F9F6483E1E6106E6009CCEBD /* item */ = { + isa = PBXGroup; + children = ( + F9F6483F1E6106E6009CCEBD /* AudioItem.swift */, + F9F648401E6106E6009CCEBD /* AudioItemQueue.swift */, + ); + path = item; + sourceTree = ""; + }; + F9F648411E6106E6009CCEBD /* player */ = { + isa = PBXGroup; + children = ( + F9F648421E6106E6009CCEBD /* AudioPlayer.swift */, + F9F648431E6106E6009CCEBD /* AudioPlayerDelegate.swift */, + F9F648441E6106E6009CCEBD /* AudioPlayerMode.swift */, + F9F648451E6106E6009CCEBD /* AudioPlayerState.swift */, + F9F648461E6106E6009CCEBD /* extensions */, + ); + path = player; + sourceTree = ""; + }; + F9F648461E6106E6009CCEBD /* extensions */ = { + isa = PBXGroup; + children = ( + F9F648471E6106E6009CCEBD /* AudioPlayer+AudioItemEvent.swift */, + F9F648481E6106E6009CCEBD /* AudioPlayer+Control.swift */, + F9F648491E6106E6009CCEBD /* AudioPlayer+CurrentItem.swift */, + F9F6484A1E6106E6009CCEBD /* AudioPlayer+NetworkEvent.swift */, + F9F6484B1E6106E6009CCEBD /* AudioPlayer+PlayerEvent.swift */, + F9F6484C1E6106E6009CCEBD /* AudioPlayer+QualityAdjustmentEvent.swift */, + F9F6484D1E6106E6009CCEBD /* AudioPlayer+Queue.swift */, + F9F6484E1E6106E6009CCEBD /* AudioPlayer+RetryEvent.swift */, + F9F6484F1E6106E6009CCEBD /* AudioPlayer+SeekEvent.swift */, + ); + path = extensions; + sourceTree = ""; + }; + F9F648511E6106E6009CCEBD /* utils */ = { + isa = PBXGroup; + children = ( + F9F648521E6106E6009CCEBD /* BackgroundHandler.swift */, + F9F648531E6106E6009CCEBD /* CMTime+TimeIntervalValue.swift */, + F9F648541E6106E6009CCEBD /* MPNowPlayingInfoCenter+AudioItem.swift */, + F9F648551E6106E6009CCEBD /* URL+Offline.swift */, + ); + path = utils; + sourceTree = ""; + }; + F9F648651E6106E6009CCEBD /* libs */ = { + isa = PBXGroup; + children = ( + F9F648661E6106E6009CCEBD /* Jukebox */, + ); + path = libs; + sourceTree = ""; + }; + F9F648661E6106E6009CCEBD /* Jukebox */ = { + isa = PBXGroup; + children = ( + F9F648671E6106E6009CCEBD /* Jukebox.swift */, + F9F648681E6106E6009CCEBD /* JukeboxItem.swift */, + ); + path = Jukebox; + sourceTree = ""; + }; + F9F648691E6106E6009CCEBD /* ui */ = { + isa = PBXGroup; + children = ( + F9F6486A1E6106E6009CCEBD /* DrawerController */, + F9F648721E6106E6009CCEBD /* IContainerController.swift */, + F9F648731E6106E6009CCEBD /* PresentationDialog */, + F9F648781E6106E6009CCEBD /* VCContainer.swift */, + F9F648791E6106E6009CCEBD /* views */, + ); + path = ui; + sourceTree = ""; + }; + F9F6486A1E6106E6009CCEBD /* DrawerController */ = { + isa = PBXGroup; + children = ( + F9F6486B1E6106E6009CCEBD /* AnimatedMenuButton.swift */, + F9F6486C1E6106E6009CCEBD /* DrawerBarButtonItem.swift */, + F9F6486D1E6106E6009CCEBD /* DrawerController.h */, + F9F6486E1E6106E6009CCEBD /* DrawerController.swift */, + F9F6486F1E6106E6009CCEBD /* DrawerVisualState.swift */, + F9F648701E6106E6009CCEBD /* DrawerVisualStateManager.swift */, + ); + path = DrawerController; + sourceTree = ""; + }; + F9F648731E6106E6009CCEBD /* PresentationDialog */ = { + isa = PBXGroup; + children = ( + F9F648741E6106E6009CCEBD /* AnimatedControllerFromBottom.swift */, + F9F648751E6106E6009CCEBD /* AnimatedTransitioning.swift */, + F9F648761E6106E6009CCEBD /* BaseUIPresentationController.swift */, + F9F648771E6106E6009CCEBD /* BaseViewControllerShowTransculent.swift */, + ); + path = PresentationDialog; + sourceTree = ""; + }; + F9F648791E6106E6009CCEBD /* views */ = { + isa = PBXGroup; + children = ( + F9F6487A1E6106E6009CCEBD /* CollectionView */, + F9F6487D1E6106E6009CCEBD /* DropDown */, + F9F648841E6106E6009CCEBD /* FloatRatingView.swift */, + F9F648851E6106E6009CCEBD /* ISRadioButton.swift */, + F9F648861E6106E6009CCEBD /* TableView */, + F9F6488E1E6106E6009CCEBD /* UIButtonCustom.swift */, + F9F6488F1E6106E6009CCEBD /* UIImageViewCustom.swift */, + F9F648901E6106E6009CCEBD /* UILabelCustom.swift */, + F9F648911E6106E6009CCEBD /* UILableCustomLeftImage.swift */, + F9F648921E6106E6009CCEBD /* UIViewCustom.swift */, + F9F648931E6106E6009CCEBD /* UIViewCustomGradientBg.swift */, + F9F648941E6106E6009CCEBD /* UIViewCustomPressHightLight.swift */, + F9F648951E6106E6009CCEBD /* VPlayerControl.swift */, + F9F648961E6106E6009CCEBD /* vTopBar */, + F9F648991E6106E6009CCEBD /* VTopNotification */, + 660BBE7B9A67082CF3CF2ED1 /* UITextViewCustom.swift */, + ); + path = views; + sourceTree = ""; + }; + F9F6487A1E6106E6009CCEBD /* CollectionView */ = { + isa = PBXGroup; + children = ( + F9F6487B1E6106E6009CCEBD /* UICollectionViewLoadData.swift */, + F9F6487C1E6106E6009CCEBD /* UICollectionViewLoading.swift */, + ); + path = CollectionView; + sourceTree = ""; + }; + F9F6487D1E6106E6009CCEBD /* DropDown */ = { + isa = PBXGroup; + children = ( + F9F6487E1E6106E6009CCEBD /* ComboBox.swift */, + F9F6487F1E6106E6009CCEBD /* DPDConstants.swift */, + F9F648801E6106E6009CCEBD /* DPDKeyboardListener.swift */, + F9F648811E6106E6009CCEBD /* DPDUIView+Extension.swift */, + F9F648821E6106E6009CCEBD /* DropDown.swift */, + F9F648831E6106E6009CCEBD /* DropDownCell.swift */, + ); + path = DropDown; + sourceTree = ""; + }; + F9F648861E6106E6009CCEBD /* TableView */ = { + isa = PBXGroup; + children = ( + F9F648871E6106E6009CCEBD /* BaseCell.swift */, + F9F648881E6106E6009CCEBD /* BaseTbLoadData.swift */, + F9F648891E6106E6009CCEBD /* CellLoadMore.swift */, + F9F6488A1E6106E6009CCEBD /* UITableViewLoadDataBg.swift */, + F9F6488B1E6106E6009CCEBD /* UITableViewLoadDataFromUIThread.swift */, + F9F6488C1E6106E6009CCEBD /* UITableViewLoading.swift */, + F9F6488D1E6106E6009CCEBD /* UITableViewLoadMore.swift */, + ); + path = TableView; + sourceTree = ""; + }; + F9F648961E6106E6009CCEBD /* vTopBar */ = { + isa = PBXGroup; + children = ( + F9F648971E6106E6009CCEBD /* ViewTopBar.swift */, + F9F648981E6106E6009CCEBD /* ViewTopBar.xib */, + ); + path = vTopBar; + sourceTree = ""; + }; + F9F648991E6106E6009CCEBD /* VTopNotification */ = { + isa = PBXGroup; + children = ( + F9F6489A1E6106E6009CCEBD /* VTopNotification.swift */, + F9F6489B1E6106E6009CCEBD /* VTopNotification.xib */, + ); + path = VTopNotification; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F9F6480F1E6105EA009CCEBD /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9F648221E6105EA009CCEBD /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + F06C0EFC82B5E473367CCBF8 /* [CP] Check Pods Manifest.lock */, + F9F6480C1E6105EA009CCEBD /* Sources */, + F9F6480D1E6105EA009CCEBD /* Frameworks */, + F9F6480E1E6105EA009CCEBD /* Resources */, + 0F9F3CDA31618C5DDD874908 /* [CP] Embed Pods Frameworks */, + 8A9D7E36BA6ECD5E089D0EDC /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = App; + productName = App; + productReference = F9F648101E6105EA009CCEBD /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F9F648081E6105EA009CCEBD /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 0820; + TargetAttributes = { + F9F6480F1E6105EA009CCEBD = { + CreatedOnToolsVersion = 8.2; + DevelopmentTeam = C52DNT23DN; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = F9F6480B1E6105EA009CCEBD /* Build configuration list for PBXProject "Morooka" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = F9F648071E6105EA009CCEBD; + productRefGroup = F9F648111E6105EA009CCEBD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F9F6480F1E6105EA009CCEBD /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F9F6480E1E6105EA009CCEBD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9F6481B1E6105EA009CCEBD /* Assets.xcassets in Resources */, + F9D8E6941E617E2E00D789BF /* Localizable.strings in Resources */, + F9F648F41E6106E6009CCEBD /* ViewTopBar.xib in Resources */, + F9F648F61E6106E6009CCEBD /* VTopNotification.xib in Resources */, + F9F648191E6105EA009CCEBD /* Main.storyboard in Resources */, + F9F648F81E6123B6009CCEBD /* VTopLogo.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0F9F3CDA31618C5DDD874908 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8A9D7E36BA6ECD5E089D0EDC /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-App/Pods-App-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + F06C0EFC82B5E473367CCBF8 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F9F6480C1E6105EA009CCEBD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9F648C61E6106E6009CCEBD /* PreferenceUtils.swift in Sources */, + F9F648A51E6106E6009CCEBD /* PlayerEventProducer.swift in Sources */, + F9F648D61E6106E6009CCEBD /* AnimatedTransitioning.swift in Sources */, + F9F648EB1E6106E6009CCEBD /* UIButtonCustom.swift in Sources */, + F9F648DE1E6106E6009CCEBD /* DPDKeyboardListener.swift in Sources */, + F9F648C81E6106E6009CCEBD /* RecordUtils.swift in Sources */, + F9F648DD1E6106E6009CCEBD /* DPDConstants.swift in Sources */, + F9F648A91E6106E6009CCEBD /* AudioItem.swift in Sources */, + F9F648B51E6106E6009CCEBD /* AudioPlayer+Queue.swift in Sources */, + F9F648DB1E6106E6009CCEBD /* UICollectionViewLoading.swift in Sources */, + F9F648C91E6106E6009CCEBD /* StringExtension.swift in Sources */, + F9F648B81E6106E6009CCEBD /* Reachability.swift in Sources */, + F9F648E01E6106E6009CCEBD /* DropDown.swift in Sources */, + F9F648F01E6106E6009CCEBD /* UIViewCustomGradientBg.swift in Sources */, + F9F648D51E6106E6009CCEBD /* AnimatedControllerFromBottom.swift in Sources */, + F9F648D21E6106E6009CCEBD /* DrawerVisualStateManager.swift in Sources */, + F9F648A71E6106E6009CCEBD /* RetryEventProducer.swift in Sources */, + F9F648ED1E6106E6009CCEBD /* UILabelCustom.swift in Sources */, + F9F648BE1E6106E6009CCEBD /* CacheUtils.swift in Sources */, + F9F648CA1E6106E6009CCEBD /* SwiftOverlays.swift in Sources */, + F9F648EE1E6106E6009CCEBD /* UILableCustomLeftImage.swift in Sources */, + F9F648B01E6106E6009CCEBD /* AudioPlayer+Control.swift in Sources */, + F9F648C41E6106E6009CCEBD /* FileUtils.swift in Sources */, + F9F648E81E6106E6009CCEBD /* UITableViewLoadDataFromUIThread.swift in Sources */, + F9F648BF1E6106E6009CCEBD /* CommonExtension.swift in Sources */, + F9F648C11E6106E6009CCEBD /* DataTypeUtils.swift in Sources */, + F9F648D91E6106E6009CCEBD /* VCContainer.swift in Sources */, + F9F648B31E6106E6009CCEBD /* AudioPlayer+PlayerEvent.swift in Sources */, + F9F648AE1E6106E6009CCEBD /* AudioPlayerState.swift in Sources */, + F9F648F21E6106E6009CCEBD /* VPlayerControl.swift in Sources */, + F9F648D41E6106E6009CCEBD /* IContainerController.swift in Sources */, + F9F648D11E6106E6009CCEBD /* DrawerVisualState.swift in Sources */, + F9F648DA1E6106E6009CCEBD /* UICollectionViewLoadData.swift in Sources */, + F9F648CF1E6106E6009CCEBD /* DrawerBarButtonItem.swift in Sources */, + F9F648F11E6106E6009CCEBD /* UIViewCustomPressHightLight.swift in Sources */, + F9F648EF1E6106E6009CCEBD /* UIViewCustom.swift in Sources */, + F9F648E71E6106E6009CCEBD /* UITableViewLoadDataBg.swift in Sources */, + F9F648EC1E6106E6009CCEBD /* UIImageViewCustom.swift in Sources */, + F9F648B21E6106E6009CCEBD /* AudioPlayer+NetworkEvent.swift in Sources */, + F9F648D01E6106E6009CCEBD /* DrawerController.swift in Sources */, + F9F648C51E6106E6009CCEBD /* NetWorkUtils.swift in Sources */, + F9F648A21E6106E6009CCEBD /* AudioItemEventProducer.swift in Sources */, + F9F648CD1E6106E6009CCEBD /* JukeboxItem.swift in Sources */, + F9F648E11E6106E6009CCEBD /* DropDownCell.swift in Sources */, + F9F648CC1E6106E6009CCEBD /* Jukebox.swift in Sources */, + F9F648B91E6106E6009CCEBD /* BackgroundHandler.swift in Sources */, + F9F648F31E6106E6009CCEBD /* ViewTopBar.swift in Sources */, + F9F648141E6105EA009CCEBD /* AppDelegate.swift in Sources */, + F9F648BD1E6106E6009CCEBD /* MediaPlayer.swift in Sources */, + F9F648AA1E6106E6009CCEBD /* AudioItemQueue.swift in Sources */, + F9F648D81E6106E6009CCEBD /* BaseViewControllerShowTransculent.swift in Sources */, + F9F648F51E6106E6009CCEBD /* VTopNotification.swift in Sources */, + F9F648C31E6106E6009CCEBD /* DownloadTask.swift in Sources */, + F9F648BC1E6106E6009CCEBD /* URL+Offline.swift in Sources */, + F9F648EA1E6106E6009CCEBD /* UITableViewLoadMore.swift in Sources */, + F9F648AC1E6106E6009CCEBD /* AudioPlayerDelegate.swift in Sources */, + F9F648AF1E6106E6009CCEBD /* AudioPlayer+AudioItemEvent.swift in Sources */, + F9F648A31E6106E6009CCEBD /* EventProducer.swift in Sources */, + F9F648E91E6106E6009CCEBD /* UITableViewLoading.swift in Sources */, + F9F648B71E6106E6009CCEBD /* AudioPlayer+SeekEvent.swift in Sources */, + F9F648AD1E6106E6009CCEBD /* AudioPlayerMode.swift in Sources */, + F9F648D71E6106E6009CCEBD /* BaseUIPresentationController.swift in Sources */, + F9F648DF1E6106E6009CCEBD /* DPDUIView+Extension.swift in Sources */, + F9F648E21E6106E6009CCEBD /* FloatRatingView.swift in Sources */, + F9F648BB1E6106E6009CCEBD /* MPNowPlayingInfoCenter+AudioItem.swift in Sources */, + F9F648DC1E6106E6009CCEBD /* ComboBox.swift in Sources */, + F9F648E31E6106E6009CCEBD /* ISRadioButton.swift in Sources */, + F9F648E51E6106E6009CCEBD /* BaseTbLoadData.swift in Sources */, + F9F648C71E6106E6009CCEBD /* Queue.swift in Sources */, + F9F648E61E6106E6009CCEBD /* CellLoadMore.swift in Sources */, + F9F648A41E6106E6009CCEBD /* NetworkEventProducer.swift in Sources */, + F9F648B41E6106E6009CCEBD /* AudioPlayer+QualityAdjustmentEvent.swift in Sources */, + F9F648B61E6106E6009CCEBD /* AudioPlayer+RetryEvent.swift in Sources */, + F9F648B11E6106E6009CCEBD /* AudioPlayer+CurrentItem.swift in Sources */, + F9F648A81E6106E6009CCEBD /* SeekEventProducer.swift in Sources */, + F9F648A61E6106E6009CCEBD /* QualityAdjustmentEventProducer.swift in Sources */, + F9F648E41E6106E6009CCEBD /* BaseCell.swift in Sources */, + F9F648BA1E6106E6009CCEBD /* CMTime+TimeIntervalValue.swift in Sources */, + F9F6482A1E610632009CCEBD /* SplashController.swift in Sources */, + F9F648C21E6106E6009CCEBD /* DialogUtils.swift in Sources */, + F9F648C01E6106E6009CCEBD /* CommonUtils.swift in Sources */, + F9F648AB1E6106E6009CCEBD /* AudioPlayer.swift in Sources */, + F9F648CE1E6106E6009CCEBD /* AnimatedMenuButton.swift in Sources */, + 660BB6A09C8708D39AFCBA42 /* Session.swift in Sources */, + 660BB91CDF86A3CB452EAED6 /* SecurityUtils.swift in Sources */, + 660BB3D4A7491C38F390AAF5 /* VCInputId.swift in Sources */, + 660BB5C735A56E6F6B18AEDB /* VTopLogo.swift in Sources */, + 660BB78D49C4DA59425EADC9 /* VCSendTraoDoi.swift in Sources */, + 660BBDAFF0683FBC3164980E /* UITextViewCustom.swift in Sources */, + 660BB48A6B73B6536343253E /* VCNoiDungTraoDoi.swift in Sources */, + 660BBA1DAD88079D5AFF4E9D /* VCRoot.swift in Sources */, + 660BB1419EB7F9A53F254A34 /* VCHome.swift in Sources */, + 660BBDD680ACA84807E94D2A /* VCContainerFullScreen.swift in Sources */, + 660BB3513899304EDAAF2011 /* VCRootCreateNew.swift in Sources */, + 660BBD91D7ED633F0D6C8F82 /* VCNhapTenCty.swift in Sources */, + 660BB1BFD392263F36E00D87 /* VCNhapTenCtruong.swift in Sources */, + 660BB1B30E2061006033A438 /* VCNhapMucChup.swift in Sources */, + 660BBCD1A02935C5843544B6 /* MucChup.swift in Sources */, + 660BB1EB08BCA748E7C80012 /* CongTruong.swift in Sources */, + 660BB033B71888619909D955 /* CellMucChupWithDelete.swift in Sources */, + 660BBD73E0E1916B0402099D /* VCChonMucChup.swift in Sources */, + 660BBFDEADB8218026818F26 /* CellChonMucChup.swift in Sources */, + 660BB1E9B67B393274F144F5 /* VCConfirmMucDaChon.swift in Sources */, + 660BBF6EB7BCBC3D7B293365 /* CellMucChupWithCamera.swift in Sources */, + 660BB6E850E4A6535EB9490E /* VCSettingBienQC.swift in Sources */, + 660BB7D60756B330D34A2882 /* VCListCtruong.swift in Sources */, + 660BB83E7195A6132D8B6406 /* VCRootListCtruong.swift in Sources */, + 660BB424710C3127B2550F70 /* CellCtruong.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + F9F648171E6105EA009CCEBD /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F9F648181E6105EA009CCEBD /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F9F648201E6105EA009CCEBD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F9F648211E6105EA009CCEBD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F9F648231E6105EA009CCEBD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BEBE6DCE6BD561EC73CCA219 /* Pods-App.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = C52DNT23DN; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.Morooka; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + F9F648241E6105EA009CCEBD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81CE8FEDDF2FC5FC5C1AD6CB /* Pods-App.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = C52DNT23DN; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.Morooka; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F9F6480B1E6105EA009CCEBD /* Build configuration list for PBXProject "Morooka" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F648201E6105EA009CCEBD /* Debug */, + F9F648211E6105EA009CCEBD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9F648221E6105EA009CCEBD /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F648231E6105EA009CCEBD /* Debug */, + F9F648241E6105EA009CCEBD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F9F648081E6105EA009CCEBD /* Project object */; +} diff --git a/Morooka.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Morooka.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..42daef8 --- /dev/null +++ b/Morooka.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Morooka.xcodeproj/project.xcworkspace/xcuserdata/ptran.xcuserdatad/UserInterfaceState.xcuserstate b/Morooka.xcodeproj/project.xcworkspace/xcuserdata/ptran.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..d5ab704f6f9e48e5eee304bef3a371645835cf40 GIT binary patch literal 12876 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 + + + + diff --git a/Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Moroka.xcscheme b/Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Moroka.xcscheme new file mode 100644 index 0000000..99aeb07 --- /dev/null +++ b/Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/Moroka.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist b/Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..0eefdfb --- /dev/null +++ b/Morooka.xcodeproj/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Moroka.xcscheme + + orderHint + 8 + + + SuppressBuildableAutocreation + + F9F6480F1E6105EA009CCEBD + + primary + + + + + diff --git a/Morooka.xcworkspace/contents.xcworkspacedata b/Morooka.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..5107ca4 --- /dev/null +++ b/Morooka.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/UserInterfaceState.xcuserstate b/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..04e149e584859b00ee1323d7ac882e6120c4e3fb GIT binary patch literal 22894 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�eMzo)da0f7J*YKTVWoxF$)HqETtmG#X8TrdXrZlxg&uN=?0{Su;m7U$am{G}mhu zYnEx2YgTCP(X7&}(QMG{)a=!~pm{@cNOM$kTysKmN^@58ndS@4*P3tgWOQ<-u-!N^ZN3(=IzLPIB$2}qj`Jt_UFBtcO>s*-e-AV=AFy?F7L;@ zKk{)tmoMad;$}h<;%`eN>=U3)W$gjz-&9BR!n%|t?nSXWulKkcQ zEAm(7ugAQ zwii8Gw72Mqq9==X#KQN+IVfEHbtAN&CzPK1==EQ ziMCGLsa>kQSG!8PT6>>%i*~Dan|6oxU)qGE|&I<2luH&SQNjn$3UP1H5%X6jmXvvlpcPMu9RSJ$nZuUn*BrrWIB zr#q_qtPGchl?^YOUe;9BTxKb|y6pP0#bry%mX_UFc6ZsmWoydrFFREBWjSB&Qyy9# zQ660$R~}!USe{&-TV7mVT3%j0s=Ts%LU~R3&hQ0f3N&Z`IqHC=&{~iFVp+z z{q!OFaD9|MRH(5LFv`ZE1!{WyJqUiQz?w} N+f6YflY+*${~vK_D*^xj literal 0 HcmV?d00001 diff --git a/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..abe55f6 --- /dev/null +++ b/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist b/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ee3458d --- /dev/null +++ b/Morooka.xcworkspace/xcuserdata/ptran.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..70d94cc --- /dev/null +++ b/Podfile @@ -0,0 +1,15 @@ +# Uncomment the next line to define a global platform for your project +#platform :ios, '9.0' + +target 'App' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + pod 'ObjectMapper', '~> 2.2' + pod 'Kingfisher', '~> 3.0' + pod 'SCLAlertView' + pod 'SwiftOverlays', '~> 3.0.0' + pod 'SwiftyJSON' + pod 'SwiftyAttributes' + pod 'Toaster', '~> 2.0' +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..d67c7b1 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,30 @@ +PODS: + - Kingfisher (3.5.0) + - ObjectMapper (2.2.3) + - SCLAlertView (0.7.0) + - SwiftOverlays (3.0.0) + - SwiftyAttributes (3.1.0) + - SwiftyJSON (3.1.4) + - Toaster (2.0.3) + +DEPENDENCIES: + - Kingfisher (~> 3.0) + - ObjectMapper (~> 2.2) + - SCLAlertView + - SwiftOverlays (~> 3.0.0) + - SwiftyAttributes + - SwiftyJSON + - Toaster (~> 2.0) + +SPEC CHECKSUMS: + Kingfisher: 7c62087039d93d26c54c8dd9b0b588d2ce55cef6 + ObjectMapper: d3b3de11267f5d971f390eb8d63dd509116a4329 + SCLAlertView: 8a14ffc40590c619e183eed2298b854385f30be1 + SwiftOverlays: 4cdd9e4f5c7817fb6348882059eb697e11685fad + SwiftyAttributes: f337a324cd21f1b18782ab85d3478ccae73f7593 + SwiftyJSON: c2842d878f95482ffceec5709abc3d05680c0220 + Toaster: 77c0b03d78e680bbbab0071c9c7ee392b0034010 + +PODFILE CHECKSUM: dc819e3ff4831b19945fa15b2eafcd86e0b0d9ac + +COCOAPODS: 1.2.0.beta.3 diff --git a/Pods/Kingfisher/LICENSE b/Pods/Kingfisher/LICENSE new file mode 100644 index 0000000..79da75d --- /dev/null +++ b/Pods/Kingfisher/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 Wei Wang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Pods/Kingfisher/README.md b/Pods/Kingfisher/README.md new file mode 100644 index 0000000..3f7ceeb --- /dev/null +++ b/Pods/Kingfisher/README.md @@ -0,0 +1,92 @@ +