├── .gitignore ├── ACKNOWLEDGEMENTS ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── Unzip.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── Karunaratne.xcuserdatad │ └── xcschemes │ ├── Unzip.xcscheme │ └── xcschememanagement.plist ├── Unzip ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── Main.storyboard ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── UnzipAction ├── Info.plist ├── MainInterface.storyboard ├── UIAlertController+UZError.h ├── UIAlertController+UZError.m ├── UZArchiveViewController.h ├── UZArchiveViewController.m ├── UZExtensionTableViewController.h ├── UZExtensionTableViewController.m ├── UZExtensionViewController.h ├── UZExtensionViewController.m ├── UZGlyphFactory.h ├── UZGlyphFactory.m ├── UZNode.h ├── UZNode.m ├── UZNodeTableViewCell.h ├── UZNodeTableViewCell.m ├── UZNodeTableViewCell.xib ├── UZNodeViewController.h ├── UZNodeViewController.m ├── UZNodeViewControllerSubclass.h ├── UZOpenInActivity.h ├── UZOpenInActivity.m ├── UZPreviewPageViewController.h ├── UZPreviewPageViewController.m ├── UZPreviewViewController.h ├── UZPreviewViewController.m ├── UZPreviewViewController.xib ├── UZUnzipCoordinator.h ├── UZUnzipCoordinator.m ├── UZUnzipOperation.h ├── UZUnzipOperation.m └── en.lproj │ └── Localizable.strings └── demo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | Unzip.xcworkspace/ 23 | Pods/ -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS: -------------------------------------------------------------------------------- 1 | Unzip uses code from the following projects: 2 | 3 | zipzap 4 | Copyright (c) 2012, Pixelglow Software. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | =================== 16 | 17 | libextobjc 18 | Copyright (c) 2012 - 2014 Justin Spahr-Summers 19 | 20 | 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: 21 | 22 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 23 | 24 | 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. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Indragie Karunaratne 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, "8.0" 2 | 3 | target "Unzip" do 4 | 5 | end 6 | 7 | target "UnzipTests" do 8 | 9 | end 10 | 11 | target "UnzipAction" do 12 | pod "zipzap", "~> 7.0" 13 | pod "libextobjc", "~> 0.4" 14 | end 15 | 16 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - libextobjc (0.4): 3 | - libextobjc/EXTADT 4 | - libextobjc/EXTConcreteProtocol 5 | - libextobjc/EXTKeyPathCoding 6 | - libextobjc/EXTNil 7 | - libextobjc/EXTSafeCategory 8 | - libextobjc/EXTScope 9 | - libextobjc/EXTSelectorChecking 10 | - libextobjc/EXTSynthesize 11 | - libextobjc/NSInvocation+EXT 12 | - libextobjc/NSMethodSignature+EXT 13 | - libextobjc/RuntimeExtensions 14 | - libextobjc/UmbrellaHeader 15 | - libextobjc/EXTADT (0.4): 16 | - libextobjc/RuntimeExtensions 17 | - libextobjc/EXTConcreteProtocol (0.4): 18 | - libextobjc/RuntimeExtensions 19 | - libextobjc/EXTKeyPathCoding (0.4): 20 | - libextobjc/RuntimeExtensions 21 | - libextobjc/EXTNil (0.4): 22 | - libextobjc/RuntimeExtensions 23 | - libextobjc/EXTSafeCategory (0.4): 24 | - libextobjc/RuntimeExtensions 25 | - libextobjc/EXTScope (0.4): 26 | - libextobjc/RuntimeExtensions 27 | - libextobjc/EXTSelectorChecking (0.4): 28 | - libextobjc/RuntimeExtensions 29 | - libextobjc/EXTSynthesize (0.4): 30 | - libextobjc/RuntimeExtensions 31 | - libextobjc/NSInvocation+EXT (0.4): 32 | - libextobjc/RuntimeExtensions 33 | - libextobjc/NSMethodSignature+EXT (0.4): 34 | - libextobjc/RuntimeExtensions 35 | - libextobjc/RuntimeExtensions (0.4) 36 | - libextobjc/UmbrellaHeader (0.4) 37 | - zipzap (7.0) 38 | 39 | DEPENDENCIES: 40 | - libextobjc (~> 0.4) 41 | - zipzap (~> 7.0) 42 | 43 | SPEC CHECKSUMS: 44 | libextobjc: ba42e4111f433273a886cd54f0ddaddb7f62f82f 45 | zipzap: 1c85484d8f89388fba8fb5824b2eae15b9ce8092 46 | 47 | COCOAPODS: 0.33.1 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Unzip 2 | ### iOS 8 Action Extension for browsing ZIP files 3 | 4 | ![Unzip Extension for iOS 8](demo.gif) 5 | 6 | This was going to become a real product until I found out that [iOS 7 added native support for ZIP files](http://www.macworld.com/article/2049370/ios-7-adds-support-for-zipped-attachments-in-mail-messages-with-quick-look.html). Nonetheless, it's a good example of how action extensions can be used to not only extend the functionality of standalone apps, but to extend the functionality of the OS as well. 7 | 8 | ### Getting Started 9 | 10 | Requires [CocoaPods](http://cocoapods.org) to manage dependencies. 11 | 12 | ``` 13 | $ cd Unzip 14 | $ pod install 15 | ``` 16 | 17 | Open **Unzip.xcworkspace**. The **Unzip** app itself doesn't contain anything, everything is implemented in **UnzipAction**. 18 | 19 | ### Icons 20 | 21 | The icons shown in the image above are from the [Glyphish 8](http://www.glyphish.com) icon set. They could not be included in this repository due to license incompatibility. 22 | 23 | ### Contact 24 | 25 | * Indragie Karunaratne 26 | * [@indragie](http://twitter.com/indragie) 27 | * [http://indragie.com](http://indragie.com) 28 | 29 | ### License 30 | 31 | Unzip is licensed under the MIT License. -------------------------------------------------------------------------------- /Unzip.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7218C48319916F4B00D7C757 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C48219916F4B00D7C757 /* main.m */; }; 11 | 7218C48619916F4B00D7C757 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C48519916F4B00D7C757 /* AppDelegate.m */; }; 12 | 7218C48919916F4B00D7C757 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C48819916F4B00D7C757 /* ViewController.m */; }; 13 | 7218C48C19916F4B00D7C757 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7218C48A19916F4B00D7C757 /* Main.storyboard */; }; 14 | 7218C48E19916F4B00D7C757 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7218C48D19916F4B00D7C757 /* Images.xcassets */; }; 15 | 7218C4AF19916F5E00D7C757 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7218C4AE19916F5E00D7C757 /* MainInterface.storyboard */; }; 16 | 7218C4B219916F5E00D7C757 /* UnzipAction.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7218C4A719916F5E00D7C757 /* UnzipAction.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 17 | 7218C4BB1991748400D7C757 /* UZArchiveViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C4BA1991748400D7C757 /* UZArchiveViewController.m */; }; 18 | 7218C4C1199176B400D7C757 /* UIAlertController+UZError.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C4C0199176B400D7C757 /* UIAlertController+UZError.m */; }; 19 | 7218C4C419917A5500D7C757 /* UZNodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C4C319917A5500D7C757 /* UZNodeViewController.m */; }; 20 | 7218C4C71991C99700D7C757 /* UZNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C4C61991C99700D7C757 /* UZNode.m */; }; 21 | 7218C4CA1991F33100D7C757 /* UZPreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7218C4C91991F33100D7C757 /* UZPreviewViewController.m */; }; 22 | 7218C4CE1991F8B700D7C757 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7218C4CC1991F8B700D7C757 /* Localizable.strings */; }; 23 | 7218C4DB1991FC3800D7C757 /* UZPreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7218C4DA1991FC3800D7C757 /* UZPreviewViewController.xib */; }; 24 | 721AE34F1995B34300246FDF /* UZPreviewPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 721AE34E1995B34300246FDF /* UZPreviewPageViewController.m */; }; 25 | 72A4BC1A1992BD8B00884AAC /* UZUnzipOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 72A4BC191992BD8B00884AAC /* UZUnzipOperation.m */; }; 26 | 72A4BC201992CA9F00884AAC /* UZNodeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 72A4BC1F1992CA9F00884AAC /* UZNodeTableViewCell.m */; }; 27 | 72A4BC231992DD1800884AAC /* UZUnzipCoordinator.m in Sources */ = {isa = PBXBuildFile; fileRef = 72A4BC221992DD1800884AAC /* UZUnzipCoordinator.m */; }; 28 | 72A4BC271993286E00884AAC /* UZExtensionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 72A4BC261993286E00884AAC /* UZExtensionViewController.m */; }; 29 | 72A4BC2A1993290D00884AAC /* UZExtensionTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 72A4BC291993290D00884AAC /* UZExtensionTableViewController.m */; }; 30 | 72A4BC2D1993314E00884AAC /* UZOpenInActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 72A4BC2C1993314E00884AAC /* UZOpenInActivity.m */; }; 31 | 72B3C10419A04B6900801EF9 /* UZGlyphFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 72B3C10319A04B6900801EF9 /* UZGlyphFactory.m */; }; 32 | 72B3C10819A050E500801EF9 /* UZNodeTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 72B3C10719A050E500801EF9 /* UZNodeTableViewCell.xib */; }; 33 | 91B01368CAFB453181A38E86 /* libPods-UnzipAction.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B27AF6F9FDC447882016106 /* libPods-UnzipAction.a */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | 7218C4B019916F5E00D7C757 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = 7218C47519916F4B00D7C757 /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = 7218C4A619916F5E00D7C757; 42 | remoteInfo = UnzipAction; 43 | }; 44 | 7218C4B319916F5E00D7C757 /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = 7218C47519916F4B00D7C757 /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = 7218C4A619916F5E00D7C757; 49 | remoteInfo = UnzipAction; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXCopyFilesBuildPhase section */ 54 | 7218C4B819916F5E00D7C757 /* Embed App Extensions */ = { 55 | isa = PBXCopyFilesBuildPhase; 56 | buildActionMask = 2147483647; 57 | dstPath = ""; 58 | dstSubfolderSpec = 13; 59 | files = ( 60 | 7218C4B219916F5E00D7C757 /* UnzipAction.appex in Embed App Extensions */, 61 | ); 62 | name = "Embed App Extensions"; 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXCopyFilesBuildPhase section */ 66 | 67 | /* Begin PBXFileReference section */ 68 | 1B27AF6F9FDC447882016106 /* libPods-UnzipAction.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnzipAction.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 637E3FFF75B74F979BBAAAC9 /* Pods-UnzipAction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnzipAction.xcconfig"; path = "Pods/Pods-UnzipAction.xcconfig"; sourceTree = ""; }; 70 | 7218C47D19916F4B00D7C757 /* Unzip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Unzip.app; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 7218C48119916F4B00D7C757 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 72 | 7218C48219916F4B00D7C757 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 73 | 7218C48419916F4B00D7C757 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 74 | 7218C48519916F4B00D7C757 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 75 | 7218C48719916F4B00D7C757 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 76 | 7218C48819916F4B00D7C757 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 77 | 7218C48B19916F4B00D7C757 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 78 | 7218C48D19916F4B00D7C757 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 79 | 7218C4A719916F5E00D7C757 /* UnzipAction.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = UnzipAction.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 7218C4AA19916F5E00D7C757 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | 7218C4AE19916F5E00D7C757 /* MainInterface.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MainInterface.storyboard; sourceTree = ""; }; 82 | 7218C4B91991748400D7C757 /* UZArchiveViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZArchiveViewController.h; sourceTree = ""; }; 83 | 7218C4BA1991748400D7C757 /* UZArchiveViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZArchiveViewController.m; sourceTree = ""; }; 84 | 7218C4BF199176B400D7C757 /* UIAlertController+UZError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertController+UZError.h"; sourceTree = ""; }; 85 | 7218C4C0199176B400D7C757 /* UIAlertController+UZError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertController+UZError.m"; sourceTree = ""; }; 86 | 7218C4C219917A5500D7C757 /* UZNodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZNodeViewController.h; sourceTree = ""; }; 87 | 7218C4C319917A5500D7C757 /* UZNodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZNodeViewController.m; sourceTree = ""; }; 88 | 7218C4C51991C99700D7C757 /* UZNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZNode.h; sourceTree = ""; }; 89 | 7218C4C61991C99700D7C757 /* UZNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZNode.m; sourceTree = ""; }; 90 | 7218C4C81991F33100D7C757 /* UZPreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZPreviewViewController.h; sourceTree = ""; }; 91 | 7218C4C91991F33100D7C757 /* UZPreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZPreviewViewController.m; sourceTree = ""; }; 92 | 7218C4CD1991F8B700D7C757 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = ""; }; 93 | 7218C4DA1991FC3800D7C757 /* UZPreviewViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UZPreviewViewController.xib; sourceTree = ""; }; 94 | 721AE34D1995B34300246FDF /* UZPreviewPageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZPreviewPageViewController.h; sourceTree = ""; }; 95 | 721AE34E1995B34300246FDF /* UZPreviewPageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZPreviewPageViewController.m; sourceTree = ""; }; 96 | 72A4BC181992BD8B00884AAC /* UZUnzipOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZUnzipOperation.h; sourceTree = ""; }; 97 | 72A4BC191992BD8B00884AAC /* UZUnzipOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZUnzipOperation.m; sourceTree = ""; }; 98 | 72A4BC1E1992CA9F00884AAC /* UZNodeTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZNodeTableViewCell.h; sourceTree = ""; }; 99 | 72A4BC1F1992CA9F00884AAC /* UZNodeTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZNodeTableViewCell.m; sourceTree = ""; }; 100 | 72A4BC211992DD1800884AAC /* UZUnzipCoordinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZUnzipCoordinator.h; sourceTree = ""; }; 101 | 72A4BC221992DD1800884AAC /* UZUnzipCoordinator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZUnzipCoordinator.m; sourceTree = ""; }; 102 | 72A4BC2419931B2700884AAC /* UZNodeViewControllerSubclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZNodeViewControllerSubclass.h; sourceTree = ""; }; 103 | 72A4BC251993286E00884AAC /* UZExtensionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZExtensionViewController.h; sourceTree = ""; }; 104 | 72A4BC261993286E00884AAC /* UZExtensionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZExtensionViewController.m; sourceTree = ""; }; 105 | 72A4BC281993290D00884AAC /* UZExtensionTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZExtensionTableViewController.h; sourceTree = ""; }; 106 | 72A4BC291993290D00884AAC /* UZExtensionTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZExtensionTableViewController.m; sourceTree = ""; }; 107 | 72A4BC2B1993314E00884AAC /* UZOpenInActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZOpenInActivity.h; sourceTree = ""; }; 108 | 72A4BC2C1993314E00884AAC /* UZOpenInActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZOpenInActivity.m; sourceTree = ""; }; 109 | 72B3C10219A04B6900801EF9 /* UZGlyphFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UZGlyphFactory.h; sourceTree = ""; }; 110 | 72B3C10319A04B6900801EF9 /* UZGlyphFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UZGlyphFactory.m; sourceTree = ""; }; 111 | 72B3C10719A050E500801EF9 /* UZNodeTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UZNodeTableViewCell.xib; sourceTree = ""; }; 112 | /* End PBXFileReference section */ 113 | 114 | /* Begin PBXFrameworksBuildPhase section */ 115 | 7218C47A19916F4B00D7C757 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | 7218C4A419916F5E00D7C757 /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | 91B01368CAFB453181A38E86 /* libPods-UnzipAction.a in Frameworks */, 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | /* End PBXFrameworksBuildPhase section */ 131 | 132 | /* Begin PBXGroup section */ 133 | 343F61C7A7264F1592D4DCA4 /* Frameworks */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 1B27AF6F9FDC447882016106 /* libPods-UnzipAction.a */, 137 | ); 138 | name = Frameworks; 139 | sourceTree = ""; 140 | }; 141 | 7218C47419916F4B00D7C757 = { 142 | isa = PBXGroup; 143 | children = ( 144 | 7218C47F19916F4B00D7C757 /* Unzip */, 145 | 7218C4A819916F5E00D7C757 /* UnzipAction */, 146 | 7218C47E19916F4B00D7C757 /* Products */, 147 | 637E3FFF75B74F979BBAAAC9 /* Pods-UnzipAction.xcconfig */, 148 | 343F61C7A7264F1592D4DCA4 /* Frameworks */, 149 | ); 150 | sourceTree = ""; 151 | }; 152 | 7218C47E19916F4B00D7C757 /* Products */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 7218C47D19916F4B00D7C757 /* Unzip.app */, 156 | 7218C4A719916F5E00D7C757 /* UnzipAction.appex */, 157 | ); 158 | name = Products; 159 | sourceTree = ""; 160 | }; 161 | 7218C47F19916F4B00D7C757 /* Unzip */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 7218C48419916F4B00D7C757 /* AppDelegate.h */, 165 | 7218C48519916F4B00D7C757 /* AppDelegate.m */, 166 | 7218C48719916F4B00D7C757 /* ViewController.h */, 167 | 7218C48819916F4B00D7C757 /* ViewController.m */, 168 | 7218C48A19916F4B00D7C757 /* Main.storyboard */, 169 | 7218C48D19916F4B00D7C757 /* Images.xcassets */, 170 | 7218C48019916F4B00D7C757 /* Supporting Files */, 171 | ); 172 | path = Unzip; 173 | sourceTree = ""; 174 | }; 175 | 7218C48019916F4B00D7C757 /* Supporting Files */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 7218C48119916F4B00D7C757 /* Info.plist */, 179 | 7218C48219916F4B00D7C757 /* main.m */, 180 | ); 181 | name = "Supporting Files"; 182 | sourceTree = ""; 183 | }; 184 | 7218C4A819916F5E00D7C757 /* UnzipAction */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 7218C4BF199176B400D7C757 /* UIAlertController+UZError.h */, 188 | 7218C4C0199176B400D7C757 /* UIAlertController+UZError.m */, 189 | 7218C4C51991C99700D7C757 /* UZNode.h */, 190 | 7218C4C61991C99700D7C757 /* UZNode.m */, 191 | 7218C4B91991748400D7C757 /* UZArchiveViewController.h */, 192 | 7218C4BA1991748400D7C757 /* UZArchiveViewController.m */, 193 | 72A4BC251993286E00884AAC /* UZExtensionViewController.h */, 194 | 72A4BC261993286E00884AAC /* UZExtensionViewController.m */, 195 | 72A4BC281993290D00884AAC /* UZExtensionTableViewController.h */, 196 | 72A4BC291993290D00884AAC /* UZExtensionTableViewController.m */, 197 | 7218C4C219917A5500D7C757 /* UZNodeViewController.h */, 198 | 7218C4C319917A5500D7C757 /* UZNodeViewController.m */, 199 | 72A4BC2419931B2700884AAC /* UZNodeViewControllerSubclass.h */, 200 | 72A4BC1E1992CA9F00884AAC /* UZNodeTableViewCell.h */, 201 | 72A4BC1F1992CA9F00884AAC /* UZNodeTableViewCell.m */, 202 | 72B3C10719A050E500801EF9 /* UZNodeTableViewCell.xib */, 203 | 7218C4C81991F33100D7C757 /* UZPreviewViewController.h */, 204 | 7218C4C91991F33100D7C757 /* UZPreviewViewController.m */, 205 | 7218C4DA1991FC3800D7C757 /* UZPreviewViewController.xib */, 206 | 721AE34D1995B34300246FDF /* UZPreviewPageViewController.h */, 207 | 721AE34E1995B34300246FDF /* UZPreviewPageViewController.m */, 208 | 72A4BC181992BD8B00884AAC /* UZUnzipOperation.h */, 209 | 72A4BC191992BD8B00884AAC /* UZUnzipOperation.m */, 210 | 72A4BC211992DD1800884AAC /* UZUnzipCoordinator.h */, 211 | 72A4BC221992DD1800884AAC /* UZUnzipCoordinator.m */, 212 | 72A4BC2B1993314E00884AAC /* UZOpenInActivity.h */, 213 | 72A4BC2C1993314E00884AAC /* UZOpenInActivity.m */, 214 | 72B3C10219A04B6900801EF9 /* UZGlyphFactory.h */, 215 | 72B3C10319A04B6900801EF9 /* UZGlyphFactory.m */, 216 | 7218C4AE19916F5E00D7C757 /* MainInterface.storyboard */, 217 | 7218C4CB1991F8B700D7C757 /* en.lproj */, 218 | 7218C4A919916F5E00D7C757 /* Supporting Files */, 219 | ); 220 | path = UnzipAction; 221 | sourceTree = ""; 222 | }; 223 | 7218C4A919916F5E00D7C757 /* Supporting Files */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | 7218C4AA19916F5E00D7C757 /* Info.plist */, 227 | ); 228 | name = "Supporting Files"; 229 | sourceTree = ""; 230 | }; 231 | 7218C4CB1991F8B700D7C757 /* en.lproj */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 7218C4CC1991F8B700D7C757 /* Localizable.strings */, 235 | ); 236 | path = en.lproj; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXGroup section */ 240 | 241 | /* Begin PBXNativeTarget section */ 242 | 7218C47C19916F4B00D7C757 /* Unzip */ = { 243 | isa = PBXNativeTarget; 244 | buildConfigurationList = 7218C49D19916F4C00D7C757 /* Build configuration list for PBXNativeTarget "Unzip" */; 245 | buildPhases = ( 246 | 7218C47919916F4B00D7C757 /* Sources */, 247 | 7218C47A19916F4B00D7C757 /* Frameworks */, 248 | 7218C47B19916F4B00D7C757 /* Resources */, 249 | 7218C4B819916F5E00D7C757 /* Embed App Extensions */, 250 | ); 251 | buildRules = ( 252 | ); 253 | dependencies = ( 254 | 7218C4B119916F5E00D7C757 /* PBXTargetDependency */, 255 | 7218C4B419916F5E00D7C757 /* PBXTargetDependency */, 256 | ); 257 | name = Unzip; 258 | productName = Unzip; 259 | productReference = 7218C47D19916F4B00D7C757 /* Unzip.app */; 260 | productType = "com.apple.product-type.application"; 261 | }; 262 | 7218C4A619916F5E00D7C757 /* UnzipAction */ = { 263 | isa = PBXNativeTarget; 264 | buildConfigurationList = 7218C4B519916F5E00D7C757 /* Build configuration list for PBXNativeTarget "UnzipAction" */; 265 | buildPhases = ( 266 | C9828A3B9C0E46ADB7F5E583 /* Check Pods Manifest.lock */, 267 | 7218C4A319916F5E00D7C757 /* Sources */, 268 | 7218C4A419916F5E00D7C757 /* Frameworks */, 269 | 7218C4A519916F5E00D7C757 /* Resources */, 270 | AF6063905CEC4BBB89105E0E /* Copy Pods Resources */, 271 | ); 272 | buildRules = ( 273 | ); 274 | dependencies = ( 275 | ); 276 | name = UnzipAction; 277 | productName = UnzipAction; 278 | productReference = 7218C4A719916F5E00D7C757 /* UnzipAction.appex */; 279 | productType = "com.apple.product-type.app-extension"; 280 | }; 281 | /* End PBXNativeTarget section */ 282 | 283 | /* Begin PBXProject section */ 284 | 7218C47519916F4B00D7C757 /* Project object */ = { 285 | isa = PBXProject; 286 | attributes = { 287 | LastUpgradeCheck = 0600; 288 | ORGANIZATIONNAME = "Indragie Karunaratne"; 289 | TargetAttributes = { 290 | 7218C47C19916F4B00D7C757 = { 291 | CreatedOnToolsVersion = 6.0; 292 | }; 293 | 7218C4A619916F5E00D7C757 = { 294 | CreatedOnToolsVersion = 6.0; 295 | DevelopmentTeam = H73VKH7W9W; 296 | }; 297 | }; 298 | }; 299 | buildConfigurationList = 7218C47819916F4B00D7C757 /* Build configuration list for PBXProject "Unzip" */; 300 | compatibilityVersion = "Xcode 3.2"; 301 | developmentRegion = English; 302 | hasScannedForEncodings = 0; 303 | knownRegions = ( 304 | en, 305 | Base, 306 | ); 307 | mainGroup = 7218C47419916F4B00D7C757; 308 | productRefGroup = 7218C47E19916F4B00D7C757 /* Products */; 309 | projectDirPath = ""; 310 | projectRoot = ""; 311 | targets = ( 312 | 7218C47C19916F4B00D7C757 /* Unzip */, 313 | 7218C4A619916F5E00D7C757 /* UnzipAction */, 314 | ); 315 | }; 316 | /* End PBXProject section */ 317 | 318 | /* Begin PBXResourcesBuildPhase section */ 319 | 7218C47B19916F4B00D7C757 /* Resources */ = { 320 | isa = PBXResourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 7218C48C19916F4B00D7C757 /* Main.storyboard in Resources */, 324 | 7218C48E19916F4B00D7C757 /* Images.xcassets in Resources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 7218C4A519916F5E00D7C757 /* Resources */ = { 329 | isa = PBXResourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 72B3C10819A050E500801EF9 /* UZNodeTableViewCell.xib in Resources */, 333 | 7218C4AF19916F5E00D7C757 /* MainInterface.storyboard in Resources */, 334 | 7218C4CE1991F8B700D7C757 /* Localizable.strings in Resources */, 335 | 7218C4DB1991FC3800D7C757 /* UZPreviewViewController.xib in Resources */, 336 | ); 337 | runOnlyForDeploymentPostprocessing = 0; 338 | }; 339 | /* End PBXResourcesBuildPhase section */ 340 | 341 | /* Begin PBXShellScriptBuildPhase section */ 342 | AF6063905CEC4BBB89105E0E /* Copy Pods Resources */ = { 343 | isa = PBXShellScriptBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | ); 347 | inputPaths = ( 348 | ); 349 | name = "Copy Pods Resources"; 350 | outputPaths = ( 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | shellPath = /bin/sh; 354 | shellScript = "\"${SRCROOT}/Pods/Pods-UnzipAction-resources.sh\"\n"; 355 | showEnvVarsInLog = 0; 356 | }; 357 | C9828A3B9C0E46ADB7F5E583 /* Check Pods Manifest.lock */ = { 358 | isa = PBXShellScriptBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | ); 362 | inputPaths = ( 363 | ); 364 | name = "Check Pods Manifest.lock"; 365 | outputPaths = ( 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | shellPath = /bin/sh; 369 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 370 | showEnvVarsInLog = 0; 371 | }; 372 | /* End PBXShellScriptBuildPhase section */ 373 | 374 | /* Begin PBXSourcesBuildPhase section */ 375 | 7218C47919916F4B00D7C757 /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | 7218C48919916F4B00D7C757 /* ViewController.m in Sources */, 380 | 7218C48619916F4B00D7C757 /* AppDelegate.m in Sources */, 381 | 7218C48319916F4B00D7C757 /* main.m in Sources */, 382 | ); 383 | runOnlyForDeploymentPostprocessing = 0; 384 | }; 385 | 7218C4A319916F5E00D7C757 /* Sources */ = { 386 | isa = PBXSourcesBuildPhase; 387 | buildActionMask = 2147483647; 388 | files = ( 389 | 72A4BC231992DD1800884AAC /* UZUnzipCoordinator.m in Sources */, 390 | 721AE34F1995B34300246FDF /* UZPreviewPageViewController.m in Sources */, 391 | 7218C4C419917A5500D7C757 /* UZNodeViewController.m in Sources */, 392 | 72A4BC1A1992BD8B00884AAC /* UZUnzipOperation.m in Sources */, 393 | 7218C4C1199176B400D7C757 /* UIAlertController+UZError.m in Sources */, 394 | 72A4BC2A1993290D00884AAC /* UZExtensionTableViewController.m in Sources */, 395 | 7218C4BB1991748400D7C757 /* UZArchiveViewController.m in Sources */, 396 | 72A4BC201992CA9F00884AAC /* UZNodeTableViewCell.m in Sources */, 397 | 7218C4CA1991F33100D7C757 /* UZPreviewViewController.m in Sources */, 398 | 7218C4C71991C99700D7C757 /* UZNode.m in Sources */, 399 | 72A4BC2D1993314E00884AAC /* UZOpenInActivity.m in Sources */, 400 | 72A4BC271993286E00884AAC /* UZExtensionViewController.m in Sources */, 401 | 72B3C10419A04B6900801EF9 /* UZGlyphFactory.m in Sources */, 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | }; 405 | /* End PBXSourcesBuildPhase section */ 406 | 407 | /* Begin PBXTargetDependency section */ 408 | 7218C4B119916F5E00D7C757 /* PBXTargetDependency */ = { 409 | isa = PBXTargetDependency; 410 | target = 7218C4A619916F5E00D7C757 /* UnzipAction */; 411 | targetProxy = 7218C4B019916F5E00D7C757 /* PBXContainerItemProxy */; 412 | }; 413 | 7218C4B419916F5E00D7C757 /* PBXTargetDependency */ = { 414 | isa = PBXTargetDependency; 415 | target = 7218C4A619916F5E00D7C757 /* UnzipAction */; 416 | targetProxy = 7218C4B319916F5E00D7C757 /* PBXContainerItemProxy */; 417 | }; 418 | /* End PBXTargetDependency section */ 419 | 420 | /* Begin PBXVariantGroup section */ 421 | 7218C48A19916F4B00D7C757 /* Main.storyboard */ = { 422 | isa = PBXVariantGroup; 423 | children = ( 424 | 7218C48B19916F4B00D7C757 /* Base */, 425 | ); 426 | name = Main.storyboard; 427 | sourceTree = ""; 428 | }; 429 | 7218C4CC1991F8B700D7C757 /* Localizable.strings */ = { 430 | isa = PBXVariantGroup; 431 | children = ( 432 | 7218C4CD1991F8B700D7C757 /* en */, 433 | ); 434 | name = Localizable.strings; 435 | sourceTree = ""; 436 | }; 437 | /* End PBXVariantGroup section */ 438 | 439 | /* Begin XCBuildConfiguration section */ 440 | 7218C49B19916F4C00D7C757 /* Debug */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | ALWAYS_SEARCH_USER_PATHS = NO; 444 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 445 | CLANG_CXX_LIBRARY = "libc++"; 446 | CLANG_ENABLE_MODULES = YES; 447 | CLANG_ENABLE_OBJC_ARC = YES; 448 | CLANG_WARN_BOOL_CONVERSION = YES; 449 | CLANG_WARN_CONSTANT_CONVERSION = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INT_CONVERSION = YES; 454 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 455 | CLANG_WARN_UNREACHABLE_CODE = YES; 456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 457 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 458 | COPY_PHASE_STRIP = NO; 459 | ENABLE_STRICT_OBJC_MSGSEND = YES; 460 | GCC_C_LANGUAGE_STANDARD = gnu99; 461 | GCC_DYNAMIC_NO_PIC = NO; 462 | GCC_OPTIMIZATION_LEVEL = 0; 463 | GCC_PREPROCESSOR_DEFINITIONS = ( 464 | "DEBUG=1", 465 | "$(inherited)", 466 | ); 467 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 468 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 469 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 470 | GCC_WARN_UNDECLARED_SELECTOR = YES; 471 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 472 | GCC_WARN_UNUSED_FUNCTION = YES; 473 | GCC_WARN_UNUSED_VARIABLE = YES; 474 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 475 | MTL_ENABLE_DEBUG_INFO = YES; 476 | ONLY_ACTIVE_ARCH = YES; 477 | SDKROOT = iphoneos; 478 | }; 479 | name = Debug; 480 | }; 481 | 7218C49C19916F4C00D7C757 /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ALWAYS_SEARCH_USER_PATHS = NO; 485 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 486 | CLANG_CXX_LIBRARY = "libc++"; 487 | CLANG_ENABLE_MODULES = YES; 488 | CLANG_ENABLE_OBJC_ARC = YES; 489 | CLANG_WARN_BOOL_CONVERSION = YES; 490 | CLANG_WARN_CONSTANT_CONVERSION = YES; 491 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 492 | CLANG_WARN_EMPTY_BODY = YES; 493 | CLANG_WARN_ENUM_CONVERSION = YES; 494 | CLANG_WARN_INT_CONVERSION = YES; 495 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 496 | CLANG_WARN_UNREACHABLE_CODE = YES; 497 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 498 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 499 | COPY_PHASE_STRIP = YES; 500 | ENABLE_NS_ASSERTIONS = NO; 501 | ENABLE_STRICT_OBJC_MSGSEND = YES; 502 | GCC_C_LANGUAGE_STANDARD = gnu99; 503 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 504 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 505 | GCC_WARN_UNDECLARED_SELECTOR = YES; 506 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 507 | GCC_WARN_UNUSED_FUNCTION = YES; 508 | GCC_WARN_UNUSED_VARIABLE = YES; 509 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 510 | MTL_ENABLE_DEBUG_INFO = NO; 511 | SDKROOT = iphoneos; 512 | VALIDATE_PRODUCT = YES; 513 | }; 514 | name = Release; 515 | }; 516 | 7218C49E19916F4C00D7C757 /* Debug */ = { 517 | isa = XCBuildConfiguration; 518 | buildSettings = { 519 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 520 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 521 | INFOPLIST_FILE = Unzip/Info.plist; 522 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 523 | PRODUCT_NAME = "$(TARGET_NAME)"; 524 | }; 525 | name = Debug; 526 | }; 527 | 7218C49F19916F4C00D7C757 /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 531 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 532 | INFOPLIST_FILE = Unzip/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | PRODUCT_NAME = "$(TARGET_NAME)"; 535 | }; 536 | name = Release; 537 | }; 538 | 7218C4B619916F5E00D7C757 /* Debug */ = { 539 | isa = XCBuildConfiguration; 540 | baseConfigurationReference = 637E3FFF75B74F979BBAAAC9 /* Pods-UnzipAction.xcconfig */; 541 | buildSettings = { 542 | CODE_SIGN_IDENTITY = "iPhone Developer"; 543 | GCC_PREPROCESSOR_DEFINITIONS = ( 544 | "DEBUG=1", 545 | "$(inherited)", 546 | ); 547 | INFOPLIST_FILE = UnzipAction/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 549 | PRODUCT_NAME = "$(TARGET_NAME)"; 550 | SKIP_INSTALL = YES; 551 | }; 552 | name = Debug; 553 | }; 554 | 7218C4B719916F5E00D7C757 /* Release */ = { 555 | isa = XCBuildConfiguration; 556 | baseConfigurationReference = 637E3FFF75B74F979BBAAAC9 /* Pods-UnzipAction.xcconfig */; 557 | buildSettings = { 558 | CODE_SIGN_IDENTITY = "iPhone Developer"; 559 | INFOPLIST_FILE = UnzipAction/Info.plist; 560 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | SKIP_INSTALL = YES; 563 | }; 564 | name = Release; 565 | }; 566 | /* End XCBuildConfiguration section */ 567 | 568 | /* Begin XCConfigurationList section */ 569 | 7218C47819916F4B00D7C757 /* Build configuration list for PBXProject "Unzip" */ = { 570 | isa = XCConfigurationList; 571 | buildConfigurations = ( 572 | 7218C49B19916F4C00D7C757 /* Debug */, 573 | 7218C49C19916F4C00D7C757 /* Release */, 574 | ); 575 | defaultConfigurationIsVisible = 0; 576 | defaultConfigurationName = Release; 577 | }; 578 | 7218C49D19916F4C00D7C757 /* Build configuration list for PBXNativeTarget "Unzip" */ = { 579 | isa = XCConfigurationList; 580 | buildConfigurations = ( 581 | 7218C49E19916F4C00D7C757 /* Debug */, 582 | 7218C49F19916F4C00D7C757 /* Release */, 583 | ); 584 | defaultConfigurationIsVisible = 0; 585 | defaultConfigurationName = Release; 586 | }; 587 | 7218C4B519916F5E00D7C757 /* Build configuration list for PBXNativeTarget "UnzipAction" */ = { 588 | isa = XCConfigurationList; 589 | buildConfigurations = ( 590 | 7218C4B619916F5E00D7C757 /* Debug */, 591 | 7218C4B719916F5E00D7C757 /* Release */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | /* End XCConfigurationList section */ 597 | }; 598 | rootObject = 7218C47519916F4B00D7C757 /* Project object */; 599 | } 600 | -------------------------------------------------------------------------------- /Unzip.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Unzip.xcodeproj/xcuserdata/Karunaratne.xcuserdatad/xcschemes/Unzip.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Unzip.xcodeproj/xcuserdata/Karunaratne.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Unzip.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | UnzipAction.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 7218C47C19916F4B00D7C757 21 | 22 | primary 23 | 24 | 25 | 7218C49219916F4C00D7C757 26 | 27 | primary 28 | 29 | 30 | 7218C4A619916F5E00D7C757 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Unzip/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Unzip/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Unzip/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Unzip/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Unzip/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Unzip/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.indragie.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Unzip/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Unzip/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Unzip/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UnzipAction/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Unzip 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | com.indragie.Unzip.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | NSExtensionActivationRule 30 | 31 | NSExtensionActivationSupportsFileWithMaxCount 32 | 1 33 | NSExtensionActivationSupportsImageWithMaxCount 34 | 0 35 | NSExtensionActivationSupportsMovieWithMaxCount 36 | 0 37 | NSExtensionActivationSupportsText 38 | 39 | NSExtensionActivationSupportsWebURLWithMaxCount 40 | 0 41 | 42 | NSExtensionPointName 43 | com.apple.ui-services 44 | NSExtensionPointVersion 45 | 1.0 46 | 47 | NSExtensionMainStoryboard 48 | MainInterface 49 | NSExtensionPointIdentifier 50 | com.apple.ui-services 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /UnzipAction/MainInterface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /UnzipAction/UIAlertController+UZError.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertController+UZError.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIAlertController (UZError) 12 | 13 | + (instancetype)uz_alertControllerWithError:(NSError *)error; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /UnzipAction/UIAlertController+UZError.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertController+UZError.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UIAlertController+UZError.h" 10 | 11 | @implementation UIAlertController (UZError) 12 | 13 | + (instancetype)uz_alertControllerWithError:(NSError *)error 14 | { 15 | UIAlertController *alert = [self alertControllerWithTitle:error.localizedDescription message:error.localizedRecoverySuggestion preferredStyle:UIAlertControllerStyleAlert]; 16 | [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:nil]]; 17 | return alert; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /UnzipAction/UZArchiveViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZArchiveViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZNodeViewController.h" 10 | 11 | @interface UZArchiveViewController : UZNodeViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /UnzipAction/UZArchiveViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZArchiveViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZArchiveViewController.h" 10 | #import "UZNodeViewControllerSubclass.h" 11 | #import "UZNode.h" 12 | #import "UZUnzipCoordinator.h" 13 | #import "UIAlertController+UZError.h" 14 | 15 | #import 16 | #import 17 | 18 | static void GetZipURLInItems(NSArray *inputItems, void (^completionHandler)(NSURL *URL, NSError *error)) 19 | { 20 | BOOL zipFound = YES; 21 | for (NSExtensionItem *item in inputItems) { 22 | for (NSItemProvider *itemProvider in item.attachments) { 23 | if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeZipArchive]) { 24 | [itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeZipArchive options:nil completionHandler:completionHandler]; 25 | zipFound = YES; 26 | break; 27 | } 28 | } 29 | if (zipFound) break; 30 | } 31 | } 32 | 33 | @interface UZArchiveViewController () 34 | @property (nonatomic, strong) ZZArchive *archive; 35 | @end 36 | 37 | @implementation UZArchiveViewController 38 | 39 | #pragma mark - View Controller Lifecycle 40 | 41 | - (void)viewDidLoad 42 | { 43 | [super viewDidLoad]; 44 | GetZipURLInItems(self.extensionContext.inputItems, ^(NSURL *URL, NSError *error) { 45 | if (error == nil) { 46 | self.archive = [ZZArchive archiveWithContentsOfURL:URL]; 47 | } else { 48 | UIAlertController *alert = [UIAlertController uz_alertControllerWithError:error]; 49 | [self presentViewController:alert animated:YES completion:nil]; 50 | } 51 | }); 52 | } 53 | 54 | #pragma mark - Accessors 55 | 56 | - (void)setArchive:(ZZArchive *)archive 57 | { 58 | if (_archive != archive) { 59 | _archive = archive; 60 | 61 | self.rootNode = [UZNode nodeWithArchive:_archive]; 62 | self.unzipCoordinator = [[UZUnzipCoordinator alloc] initWithArchive:_archive]; 63 | self.navigationItem.title = _archive.URL.lastPathComponent; 64 | } 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /UnzipAction/UZExtensionTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZExtensionTableViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UZExtensionTableViewController : UITableViewController 12 | 13 | - (instancetype)initWithStyle:(UITableViewStyle)style extensionContext:(NSExtensionContext *)extensionContext; 14 | 15 | @property (nonatomic, strong, readonly) NSExtensionContext *uz_extensionContext; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /UnzipAction/UZExtensionTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZExtensionTableViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZExtensionTableViewController.h" 10 | 11 | @implementation UZExtensionTableViewController 12 | @synthesize uz_extensionContext = _uz_extensionContext; 13 | 14 | - (instancetype)initWithStyle:(UITableViewStyle)style extensionContext:(NSExtensionContext *)extensionContext 15 | { 16 | if ((self = [super initWithStyle:style])) { 17 | _uz_extensionContext = extensionContext; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)viewDidLoad 23 | { 24 | [super viewDidLoad]; 25 | if (self.uz_extensionContext != nil) { 26 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Close", nil) style:UIBarButtonItemStylePlain target:self action:@selector(close)]; 27 | } 28 | } 29 | 30 | - (NSExtensionContext *)uz_extensionContext 31 | { 32 | return self.extensionContext ?: _uz_extensionContext; 33 | } 34 | 35 | + (NSSet *)keyPathsForValuesAffectingUz_extensionContext 36 | { 37 | return [NSSet setWithObject:@"extensionContext"]; 38 | } 39 | 40 | - (void)close 41 | { 42 | [self.uz_extensionContext completeRequestReturningItems:self.uz_extensionContext.inputItems completionHandler:nil]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /UnzipAction/UZExtensionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZExtensionViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UZExtensionViewController : UIViewController 12 | 13 | - (instancetype)initWithExtensionContext:(NSExtensionContext *)extensionContext; 14 | 15 | @property (nonatomic, strong, readonly) NSExtensionContext *uz_extensionContext; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /UnzipAction/UZExtensionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZExtensionViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZExtensionViewController.h" 10 | 11 | @implementation UZExtensionViewController 12 | @synthesize uz_extensionContext = _uz_extensionContext; 13 | 14 | - (instancetype)initWithExtensionContext:(NSExtensionContext *)extensionContext 15 | { 16 | if ((self = [super initWithNibName:nil bundle:nil])) { 17 | _uz_extensionContext = extensionContext; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)viewDidLoad 23 | { 24 | [super viewDidLoad]; 25 | if (self.uz_extensionContext != nil) { 26 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Close", nil) style:UIBarButtonItemStylePlain target:self action:@selector(close)]; 27 | } 28 | } 29 | 30 | - (NSExtensionContext *)uz_extensionContext 31 | { 32 | return self.extensionContext ?: _uz_extensionContext; 33 | } 34 | 35 | + (NSSet *)keyPathsForValuesAffectingUz_extensionContext 36 | { 37 | return [NSSet setWithObject:@"extensionContext"]; 38 | } 39 | 40 | - (void)close 41 | { 42 | [self.uz_extensionContext completeRequestReturningItems:self.uz_extensionContext.inputItems completionHandler:nil]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /UnzipAction/UZGlyphFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZGlyphFactory.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/16/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #ifndef UZGlyphFactory_h 12 | #define UZGlyphFactory_h 13 | 14 | UIImage * UZDirectoryGlyphImage(UIColor *tintColor); 15 | UIImage * UZFileGlyphImage(NSString *fileName, UIColor *tintColor); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /UnzipAction/UZGlyphFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZGlyphFactory.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/16/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZGlyphFactory.h" 10 | #import 11 | 12 | static NSMutableDictionary *_tintedImages = nil; 13 | 14 | static NSString * FileTypeForUTI(CFStringRef UTI) 15 | { 16 | NSString *fileType = nil; 17 | if (UTTypeConformsTo(UTI, kUTTypeArchive)) { 18 | fileType = @"Archive"; 19 | } else if (UTTypeConformsTo(UTI, kUTTypeAudio)) { 20 | fileType = @"Audio"; 21 | } else if (UTTypeConformsTo(UTI, kUTTypeSourceCode)) { 22 | fileType = @"Code"; 23 | } else if (UTTypeConformsTo(UTI, kUTTypeDirectory)) { 24 | fileType = @"Directory"; 25 | } else if (UTTypeConformsTo(UTI, kUTTypeFont)) { 26 | fileType = @"Font"; 27 | } else if (UTTypeConformsTo(UTI, kUTTypeImage)) { 28 | fileType = @"Image"; 29 | } else if (UTTypeConformsTo(UTI, kUTTypePresentation)) { 30 | fileType = @"Presentation"; 31 | } else if (UTTypeConformsTo(UTI, kUTTypeSpreadsheet)) { 32 | fileType = @"Spreadsheet"; 33 | } else if (UTTypeConformsTo(UTI, kUTTypeVideo)) { 34 | fileType = @"Video"; 35 | } else if (UTTypeConformsTo(UTI, kUTTypeXML) || UTTypeConformsTo(UTI, kUTTypeHTML)) { 36 | fileType = @"XML"; 37 | } else { 38 | fileType = @"Generic"; 39 | } 40 | return fileType; 41 | } 42 | 43 | static UIImage * MaskImageForUTI(CFStringRef UTI) 44 | { 45 | return [UIImage imageNamed:[NSString stringWithFormat:@"FileType%@", FileTypeForUTI(UTI)]]; 46 | } 47 | 48 | static UIImage * TintedImageFromMask(UIImage *mask, UIColor *tintColor) 49 | { 50 | UIGraphicsBeginImageContextWithOptions(mask.size, NO, mask.scale); 51 | 52 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 53 | const CGRect bounds = (CGRect){ .size = mask.size }; 54 | 55 | CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(bounds)); 56 | CGContextConcatCTM(ctx, flipVertical); 57 | 58 | CGContextClipToMask(ctx, bounds, mask.CGImage); 59 | [tintColor set]; 60 | UIRectFill(bounds); 61 | 62 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 63 | UIGraphicsEndImageContext(); 64 | return image; 65 | } 66 | 67 | static UIColor * SystemDefaultTintColor() 68 | { 69 | static UIColor *tintColor = nil; 70 | static dispatch_once_t onceToken; 71 | dispatch_once(&onceToken, ^{ 72 | tintColor = [[[UIView alloc] initWithFrame:CGRectZero] tintColor]; 73 | }); 74 | return tintColor; 75 | } 76 | 77 | static UIImage * GlyphImageForUTI(CFStringRef UTI) 78 | { 79 | NSCAssert([NSThread isMainThread], @"This function can only be called from the main thread."); 80 | 81 | UIImage *image = _tintedImages[(__bridge NSString *)UTI]; 82 | if (image == nil) { 83 | image = TintedImageFromMask(MaskImageForUTI(UTI), SystemDefaultTintColor()); 84 | if (_tintedImages == nil) { 85 | _tintedImages = [[NSMutableDictionary alloc] init]; 86 | } 87 | _tintedImages[(__bridge NSString *)UTI] = image; 88 | } 89 | return image; 90 | } 91 | 92 | UIImage * UZDirectoryGlyphImage(UIColor *tintColor) 93 | { 94 | return GlyphImageForUTI(kUTTypeDirectory); 95 | } 96 | 97 | UIImage * UZFileGlyphImage(NSString *fileName, UIColor *tintColor) 98 | { 99 | CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileName.pathExtension, NULL); 100 | UIImage *image = GlyphImageForUTI(UTI); 101 | CFRelease(UTI); 102 | return image; 103 | } 104 | -------------------------------------------------------------------------------- /UnzipAction/UZNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZNode.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ZZArchiveEntry; 12 | @class ZZArchive; 13 | 14 | @interface UZNode : NSObject 15 | 16 | @property (nonatomic, strong, readonly) NSArray *children; 17 | @property (nonatomic, assign, readonly, getter=isDirectory) BOOL directory; 18 | @property (nonatomic, assign, readonly, getter=isEncrypted) BOOL encrypted; 19 | @property (nonatomic, assign, readonly) NSUInteger uncompressedSize; 20 | @property (nonatomic, copy, readonly) NSString *fileName; 21 | 22 | + (instancetype)nodeWithArchive:(ZZArchive *)archive; 23 | 24 | - (NSInputStream *)streamWithPassword:(NSString *)password error:(NSError **)error; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /UnzipAction/UZNode.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZNode.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZNode.h" 10 | 11 | #import 12 | 13 | @interface UZNodeEntryContainer : NSObject 14 | @property (nonatomic, strong) ZZArchiveEntry *entry; 15 | @property (nonatomic, strong, readonly) NSMutableArray *mutableChildren; 16 | @end 17 | 18 | @implementation UZNodeEntryContainer 19 | 20 | - (instancetype)initWithEntry:(ZZArchiveEntry *)entry 21 | { 22 | if ((self = [super init])) { 23 | _entry = entry; 24 | _mutableChildren = [[NSMutableArray alloc] init]; 25 | } 26 | return self; 27 | } 28 | 29 | @end 30 | 31 | @interface UZNode () 32 | - (instancetype)initWithEntry:(ZZArchiveEntry *)entry level:(NSUInteger)level children:(NSArray *)children; 33 | @property (nonatomic, strong, readonly) ZZArchiveEntry *archiveEntry; 34 | @end 35 | 36 | static BOOL EntryIsDirectory(ZZArchiveEntry *entry) 37 | { 38 | return (entry.uncompressedSize == 0) && !entry.compressed; 39 | } 40 | 41 | static NSArray * IgnoredFilenames() 42 | { 43 | static NSArray *filenames = nil; 44 | static dispatch_once_t onceToken; 45 | dispatch_once(&onceToken, ^{ 46 | filenames = @[@"__MACOSX", 47 | @".DS_Store"]; 48 | }); 49 | return filenames; 50 | } 51 | 52 | static UZNode * NodeForEntries(NSArray *entries, ZZArchiveEntry *parent, NSUInteger level) 53 | { 54 | NSArray *ignoredFilenames = IgnoredFilenames(); 55 | NSMutableDictionary *nameToEntryContainerMapping = [[NSMutableDictionary alloc] init]; 56 | NSMutableArray *containers = [[NSMutableArray alloc] init]; 57 | 58 | for (ZZArchiveEntry *entry in entries) { 59 | NSArray *components = entry.fileName.pathComponents; 60 | if (components.count == 0) continue; 61 | 62 | NSString *fileName = components[level]; 63 | if ([ignoredFilenames containsObject:fileName]) continue; 64 | 65 | UZNodeEntryContainer *container = nameToEntryContainerMapping[fileName]; 66 | if (container == nil) { 67 | container = [[UZNodeEntryContainer alloc] initWithEntry:entry]; 68 | nameToEntryContainerMapping[fileName] = container; 69 | [containers addObject:container]; 70 | } else { 71 | [container.mutableChildren addObject:entry]; 72 | } 73 | } 74 | 75 | NSMutableArray *children = [[NSMutableArray alloc] init]; 76 | for (UZNodeEntryContainer *container in containers) { 77 | UZNode *node = NodeForEntries(container.mutableChildren, container.entry, level + 1); 78 | [children addObject:node]; 79 | } 80 | 81 | return [[UZNode alloc] initWithEntry:parent level:level children:children]; 82 | } 83 | 84 | static NSString * IndentationString(NSUInteger level) 85 | { 86 | NSMutableString *string = [[NSMutableString alloc] init]; 87 | for (NSInteger i = 0; i < level; i++) { 88 | [string appendString:@"----"]; 89 | } 90 | return string; 91 | } 92 | 93 | static void PrintPrettyHierarchicalRepresentation(UZNode *node, NSMutableString *string, NSUInteger level) 94 | { 95 | if (node.fileName.length != 0) { 96 | [string appendString:IndentationString(level)]; 97 | [string appendFormat:@" %@\n", node.fileName]; 98 | } 99 | 100 | for (UZNode *child in node.children) { 101 | PrintPrettyHierarchicalRepresentation(child, string, level + 1); 102 | } 103 | } 104 | 105 | @implementation UZNode 106 | 107 | - (instancetype)initWithEntry:(ZZArchiveEntry *)entry level:(NSUInteger)level children:(NSArray *)children 108 | { 109 | if ((self = [super init])) { 110 | _directory = EntryIsDirectory(entry); 111 | _encrypted = entry.encrypted; 112 | _uncompressedSize = entry.uncompressedSize; 113 | if (level >= 1) { 114 | _fileName = entry.fileName.pathComponents[level - 1]; 115 | } 116 | _children = children; 117 | _archiveEntry = entry; 118 | } 119 | return self; 120 | } 121 | 122 | + (instancetype)nodeWithArchive:(ZZArchive *)archive 123 | { 124 | return NodeForEntries(archive.entries, nil, 0); 125 | } 126 | 127 | - (NSInputStream *)streamWithPassword:(NSString *)password error:(NSError **)error 128 | { 129 | return [self.archiveEntry newStreamWithPassword:password error:error]; 130 | } 131 | 132 | #pragma mark - NSObject 133 | 134 | - (NSString *)description 135 | { 136 | NSMutableString *description = [[NSMutableString alloc] init]; 137 | PrintPrettyHierarchicalRepresentation(self, description, 0); 138 | return description; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /UnzipAction/UZNodeTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZNodeTableViewCell.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UZNodeTableViewCell : UITableViewCell 12 | 13 | @property (nonatomic, weak) IBOutlet UIImageView *glyphImageView; 14 | @property (nonatomic, weak) IBOutlet UILabel *fileNameLabel; 15 | @property (nonatomic, weak) IBOutlet UILabel *fileSizeLabel; 16 | 17 | + (NSString *)reuseIdentifier; 18 | + (CGFloat)rowHeight; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /UnzipAction/UZNodeTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZNodeTableViewCell.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZNodeTableViewCell.h" 10 | 11 | @implementation UZNodeTableViewCell 12 | 13 | + (NSString *)reuseIdentifier 14 | { 15 | return NSStringFromClass(self.class); 16 | } 17 | 18 | + (CGFloat)rowHeight 19 | { 20 | return 52.0; 21 | } 22 | 23 | #pragma mark - UITableViewCell 24 | 25 | - (void)prepareForReuse 26 | { 27 | self.fileNameLabel.text = nil; 28 | self.fileSizeLabel.text = nil; 29 | self.glyphImageView.image = nil; 30 | [super prepareForReuse]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /UnzipAction/UZNodeTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /UnzipAction/UZNodeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZNodeViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZExtensionTableViewController.h" 10 | 11 | @class UZNode; 12 | @class UZUnzipCoordinator; 13 | 14 | @interface UZNodeViewController : UZExtensionTableViewController 15 | 16 | - (instancetype)initWithRootNode:(UZNode *)rootNode 17 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 18 | extensionContext:(NSExtensionContext *)extensionContext; 19 | 20 | @property (nonatomic, strong, readonly) UZNode *rootNode; 21 | @property (nonatomic, strong, readonly) UZUnzipCoordinator *unzipCoordinator; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /UnzipAction/UZNodeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZNodeViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZNodeViewController.h" 10 | #import "UZNodeViewControllerSubclass.h" 11 | #import "UZNodeTableViewCell.h" 12 | #import "UZPreviewViewController.h" 13 | #import "UZNode.h" 14 | #import "UZUnzipCoordinator.h" 15 | #import "UZGlyphFactory.h" 16 | 17 | const CGFloat kSearchBarHeight = 44.0; 18 | 19 | @interface UZNodeViewController () 20 | @property (nonatomic, strong) NSArray *sections; 21 | @property (nonatomic, strong, readonly) UILocalizedIndexedCollation *collation; 22 | @property (nonatomic, strong, readonly) NSByteCountFormatter *byteCountFormatter; 23 | 24 | @property (nonatomic, weak, readonly) UZNodeViewController *parentNodeViewController; 25 | @property (nonatomic, strong) UISearchController *searchController; 26 | @property (nonatomic, assign, readonly, getter=isSearchResultsController) BOOL searchResultsController; 27 | @property (nonatomic, assign, readonly, getter=isSearching) BOOL searching; 28 | @property (nonatomic, strong) NSString *previousSearchQuery; 29 | @property (nonatomic, strong) NSArray *filteredResults; 30 | @end 31 | 32 | static NSArray * SectionsForNode(NSArray *children, UILocalizedIndexedCollation *collation) 33 | { 34 | NSMutableArray *sections = [[NSMutableArray alloc] init]; 35 | const SEL stringSelector = @selector(fileName); 36 | const NSUInteger titleCount = collation.sectionIndexTitles.count; 37 | 38 | for (NSUInteger i = 0; i < titleCount; i++) { 39 | [sections addObject:[[NSMutableArray alloc] init]]; 40 | } 41 | 42 | for (UZNode *child in children) { 43 | NSInteger sectionIndex = [collation sectionForObject:child collationStringSelector:stringSelector]; 44 | [sections[sectionIndex] addObject:child]; 45 | } 46 | 47 | for (NSUInteger i = 0; i < titleCount; i++) { 48 | NSArray *sortedChildren = [collation sortedArrayFromArray:sections[i] collationStringSelector:stringSelector]; 49 | [sections replaceObjectAtIndex:i withObject:sortedChildren]; 50 | } 51 | 52 | return sections; 53 | } 54 | 55 | static NSPredicate * FilterPredicate(NSString *searchQuery) 56 | { 57 | return [NSPredicate predicateWithBlock:^BOOL(UZNode *node, NSDictionary *bindings) { 58 | NSRange range = [node.fileName rangeOfString:searchQuery options:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch]; 59 | return (range.location != NSNotFound); 60 | }]; 61 | } 62 | 63 | static NSArray * FilteredChildren(NSArray *children, NSString *searchQuery) 64 | { 65 | return [children filteredArrayUsingPredicate:FilterPredicate(searchQuery)]; 66 | } 67 | 68 | @implementation UZNodeViewController 69 | 70 | - (instancetype)initWithRootNode:(UZNode *)rootNode 71 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 72 | extensionContext:(NSExtensionContext *)extensionContext 73 | { 74 | return [self initWithRootNode:rootNode unzipCoordinator:unzipCoordinator extensionContext:extensionContext parentNodeViewController:nil]; 75 | } 76 | 77 | - (instancetype)initWithRootNode:(UZNode *)rootNode 78 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 79 | extensionContext:(NSExtensionContext *)extensionContext 80 | parentNodeViewController:(UZNodeViewController *)parentNodeViewController 81 | { 82 | if ((self = [self initWithStyle:UITableViewStylePlain extensionContext:extensionContext parentNodeViewController:parentNodeViewController])) { 83 | self.unzipCoordinator = unzipCoordinator; 84 | self.rootNode = rootNode; 85 | } 86 | return self; 87 | } 88 | 89 | - (instancetype)initWithStyle:(UITableViewStyle)style 90 | extensionContext:(NSExtensionContext *)extensionContext 91 | parentNodeViewController:(UZNodeViewController *)parentNodeViewController 92 | { 93 | if ((self = [super initWithStyle:style extensionContext:extensionContext])) { 94 | _parentNodeViewController = parentNodeViewController; 95 | [self commonInit_UZNodeViewController]; 96 | } 97 | return self; 98 | } 99 | 100 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 101 | { 102 | if ((self = [super initWithCoder:aDecoder])) { 103 | [self commonInit_UZNodeViewController]; 104 | } 105 | return self; 106 | } 107 | 108 | - (void)commonInit_UZNodeViewController 109 | { 110 | _collation = UILocalizedIndexedCollation.currentCollation; 111 | _byteCountFormatter = [[NSByteCountFormatter alloc] init]; 112 | self.definesPresentationContext = YES; 113 | } 114 | 115 | - (void)viewDidLoad 116 | { 117 | [super viewDidLoad]; 118 | 119 | UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero]; 120 | backgroundView.backgroundColor = UIColor.whiteColor; 121 | self.tableView.backgroundView = backgroundView; 122 | 123 | UINib *nib = [UINib nibWithNibName:NSStringFromClass(UZNodeTableViewCell.class) bundle:nil]; 124 | [self.tableView registerNib:nib forCellReuseIdentifier:UZNodeTableViewCell.reuseIdentifier]; 125 | self.tableView.rowHeight = UZNodeTableViewCell.rowHeight; 126 | 127 | [self configureAndDisplaySearchBar]; 128 | } 129 | 130 | - (void)viewWillAppear:(BOOL)animated 131 | { 132 | [super viewWillAppear:animated]; 133 | 134 | if (self.searchController != nil) { 135 | self.tableView.contentOffset = (CGPoint){ .y = kSearchBarHeight }; 136 | } 137 | } 138 | 139 | #pragma mark - Accessors 140 | 141 | - (void)setRootNode:(UZNode *)rootNode 142 | { 143 | if (_rootNode != rootNode) { 144 | self.searchController = nil; 145 | 146 | _rootNode = rootNode; 147 | 148 | if (![self isSearchResultsController]) { 149 | [self createSearchController]; 150 | } 151 | 152 | self.navigationItem.title = _rootNode.fileName; 153 | [self rebuildSections]; 154 | } 155 | } 156 | 157 | - (void)setSearchQuery:(NSString *)searchQuery 158 | { 159 | if (_searchQuery != searchQuery) { 160 | if (searchQuery.length) { 161 | self.previousSearchQuery = _searchQuery; 162 | } else { 163 | self.previousSearchQuery = nil; 164 | } 165 | 166 | _searchQuery = searchQuery; 167 | [self rebuildSections]; 168 | } 169 | } 170 | 171 | - (BOOL)isSearching 172 | { 173 | return (self.searchQuery.length != 0); 174 | } 175 | 176 | + (NSSet *)keyPathsForValuesAffectingSearching 177 | { 178 | return [NSSet setWithObject:@"searchQuery"]; 179 | } 180 | 181 | - (BOOL)isSearchResultsController 182 | { 183 | return (self.parentNodeViewController != nil); 184 | } 185 | 186 | + (NSSet *)keyPathsForValuesAffectingSearchResultsController 187 | { 188 | return [NSSet setWithObject:@"parentNodeViewController"]; 189 | } 190 | 191 | #pragma mark - UITableViewDataSource 192 | 193 | - (UZNode *)nodeAtIndexPath:(NSIndexPath *)indexPath 194 | { 195 | NSArray *children = self.sections[indexPath.section]; 196 | return children[indexPath.row]; 197 | } 198 | 199 | - (void)rebuildSections 200 | { 201 | NSArray *children = nil; 202 | if (self.searching) { 203 | if (self.previousSearchQuery != nil && [self.searchQuery hasPrefix:self.previousSearchQuery]) { 204 | children = FilteredChildren(self.filteredResults, self.searchQuery); 205 | } else { 206 | children = FilteredChildren(self.rootNode.children, self.searchQuery); 207 | self.filteredResults = children; 208 | } 209 | } else { 210 | children = self.rootNode.children; 211 | self.filteredResults = nil; 212 | } 213 | 214 | self.sections = SectionsForNode(children, self.collation); 215 | [self.tableView reloadData]; 216 | } 217 | 218 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 219 | { 220 | return self.sections.count; 221 | } 222 | 223 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 224 | { 225 | return [self.sections[section] count]; 226 | } 227 | 228 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 229 | { 230 | if ([self.sections[section] count] != 0) { 231 | return self.collation.sectionTitles[section]; 232 | } 233 | return nil; 234 | } 235 | 236 | - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView 237 | { 238 | return self.collation.sectionIndexTitles; 239 | } 240 | 241 | - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index 242 | { 243 | return [self.collation sectionForSectionIndexTitleAtIndex:index]; 244 | } 245 | 246 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 247 | { 248 | UZNodeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:UZNodeTableViewCell.reuseIdentifier]; 249 | UZNode *node = [self nodeAtIndexPath:indexPath]; 250 | 251 | cell.fileNameLabel.text = node.fileName; 252 | if (node.directory) { 253 | cell.fileNameLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; 254 | cell.fileSizeLabel.text = nil; 255 | cell.glyphImageView.image = UZDirectoryGlyphImage(nil); 256 | } else { 257 | cell.fileNameLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; 258 | cell.fileSizeLabel.text = [self.byteCountFormatter stringFromByteCount:node.uncompressedSize]; 259 | cell.glyphImageView.image = UZFileGlyphImage(node.fileName, UIColor.blueColor); 260 | } 261 | 262 | return cell; 263 | } 264 | 265 | #pragma mark - UITableViewDelegate 266 | 267 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 268 | { 269 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 270 | 271 | UZNode *node = [self nodeAtIndexPath:indexPath]; 272 | if (node.directory) { 273 | UZNodeViewController *viewController = [[self.class alloc] initWithRootNode:node unzipCoordinator:self.unzipCoordinator extensionContext:self.uz_extensionContext]; 274 | [self pushViewController:viewController animated:YES]; 275 | } else if (node.encrypted) { 276 | [self presentPasswordAlertForNode:node completionHandler:^(NSString *password) { 277 | if (password != nil) { 278 | [self pushPreviewControllerWithNode:node password:password]; 279 | } 280 | }]; 281 | } else { 282 | [self pushPreviewControllerWithNode:node password:nil]; 283 | } 284 | } 285 | 286 | - (void)pushPreviewControllerWithNode:(UZNode *)node password:(NSString *)password 287 | { 288 | UZPreviewViewController *viewController = [[UZPreviewViewController alloc] initWithNode:node password:password unzipCoordinator:self.unzipCoordinator extensionContext:self.uz_extensionContext]; 289 | [self pushViewController:viewController animated:YES]; 290 | } 291 | 292 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated 293 | { 294 | UINavigationController *navigationController = self.navigationController ?: self.parentNodeViewController.navigationController; 295 | [navigationController pushViewController:viewController animated:animated]; 296 | } 297 | 298 | #pragma mark - Search 299 | 300 | - (void)createSearchController 301 | { 302 | UZNodeViewController *searchResultsController = [[UZNodeViewController alloc] initWithRootNode:_rootNode unzipCoordinator:self.unzipCoordinator extensionContext:nil parentNodeViewController:self]; 303 | self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController]; 304 | self.searchController.searchResultsUpdater = searchResultsController; 305 | [self configureAndDisplaySearchBar]; 306 | } 307 | 308 | - (void)configureAndDisplaySearchBar 309 | { 310 | if (self.tableView.tableHeaderView != nil || self.searchController == nil) return; 311 | 312 | UISearchBar *searchBar = self.searchController.searchBar; 313 | searchBar.searchBarStyle = UISearchBarStyleMinimal; 314 | 315 | CGRect searchBarFrame = searchBar.frame; 316 | searchBarFrame.size.height = kSearchBarHeight; 317 | searchBar.frame = searchBarFrame; 318 | 319 | self.tableView.tableHeaderView = searchBar; 320 | } 321 | 322 | #pragma mark - UISearchResultsUpdating 323 | 324 | - (void)updateSearchResultsForSearchController:(UISearchController *)searchController 325 | { 326 | UZNodeViewController *viewController = (UZNodeViewController *)searchController.searchResultsController; 327 | viewController.searchQuery = searchController.searchBar.text; 328 | } 329 | 330 | #pragma mark - Encryption 331 | 332 | - (void)presentPasswordAlertForNode:(UZNode *)node completionHandler:(void (^)(NSString *password))completionHandler 333 | { 334 | NSParameterAssert(completionHandler); 335 | 336 | NSString *message = [NSString stringWithFormat:NSLocalizedString(@"EncryptionAlertMessage", nil), node.fileName]; 337 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"EncryptionAlertTitle", nil) message:message preferredStyle:UIAlertControllerStyleAlert]; 338 | [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { 339 | textField.secureTextEntry = YES; 340 | }]; 341 | 342 | __weak UIAlertController *weakAlert = alert; 343 | [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 344 | completionHandler([weakAlert.textFields[0] text]); 345 | [self dismissViewControllerAnimated:YES completion:nil]; 346 | }]]; 347 | [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { 348 | completionHandler(nil); 349 | [self dismissViewControllerAnimated:YES completion:nil]; 350 | }]]; 351 | 352 | [self presentViewController:alert animated:YES completion:nil]; 353 | } 354 | 355 | @end 356 | -------------------------------------------------------------------------------- /UnzipAction/UZNodeViewControllerSubclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZNodeViewControllerSubclass.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #ifndef Unzip_UZNodeViewControllerSubclass_h 10 | #define Unzip_UZNodeViewControllerSubclass_h 11 | 12 | @interface UZNodeViewController () 13 | @property (nonatomic, strong, readwrite) UZNode *rootNode; 14 | @property (nonatomic, strong, readwrite) UZUnzipCoordinator *unzipCoordinator; 15 | @property (nonatomic, strong) NSString *searchQuery; 16 | 17 | - (instancetype)initWithRootNode:(UZNode *)rootNode 18 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 19 | extensionContext:(NSExtensionContext *)extensionContext 20 | parentNodeViewController:(UZNodeViewController *)parentNodeViewController; 21 | 22 | @end 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /UnzipAction/UZOpenInActivity.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZOpenInActivity.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UZOpenInActivity : UIActivity 12 | 13 | - (instancetype)initWithBarButtonItem:(UIBarButtonItem *)barButtonItem; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /UnzipAction/UZOpenInActivity.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZOpenInActivity.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZOpenInActivity.h" 10 | 11 | @interface UZOpenInActivity () 12 | @property (nonatomic, weak, readonly) UIBarButtonItem *barButtonItem; 13 | @property (nonatomic, strong) NSURL *documentURL; 14 | @property (nonatomic, strong) UIDocumentInteractionController *docInteractionController; 15 | @end 16 | 17 | @implementation UZOpenInActivity 18 | 19 | - (instancetype)initWithBarButtonItem:(UIBarButtonItem *)barButtonItem 20 | { 21 | if ((self = [super init])) { 22 | _barButtonItem = barButtonItem; 23 | } 24 | return self; 25 | } 26 | 27 | #pragma mark - UIActivity 28 | 29 | + (UIActivityCategory)activityCategory 30 | { 31 | return UIActivityCategoryAction; 32 | } 33 | 34 | - (NSString *)activityType 35 | { 36 | return NSStringFromClass(self.class); 37 | } 38 | 39 | - (NSString *)activityTitle 40 | { 41 | return NSLocalizedString(@"Open In", nil); 42 | } 43 | 44 | - (UIImage *)activityImage 45 | { 46 | return nil; // TODO: Put it an icon 47 | } 48 | 49 | - (BOOL)canPerformWithActivityItems:(NSArray *)activityItems 50 | { 51 | if (activityItems.count != 1) return NO; 52 | return [activityItems[0] isKindOfClass:NSURL.class]; 53 | } 54 | 55 | - (void)prepareWithActivityItems:(NSArray *)activityItems 56 | { 57 | self.documentURL = activityItems[0]; 58 | } 59 | 60 | - (void)performActivity 61 | { 62 | self.docInteractionController = [UIDocumentInteractionController interactionControllerWithURL:self.documentURL]; 63 | self.docInteractionController.delegate = self; 64 | [self.docInteractionController presentOpenInMenuFromBarButtonItem:self.barButtonItem animated:YES]; 65 | } 66 | 67 | #pragma mark - UIDocumentInteractionControllerDelegate 68 | 69 | - (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller 70 | { 71 | self.docInteractionController = nil; 72 | [self activityDidFinish:YES]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /UnzipAction/UZPreviewPageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZPreviewPageViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/8/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UZNode; 12 | @class UZUnzipCoordinator; 13 | 14 | @interface UZPreviewPageViewController : UIPageViewController 15 | 16 | - (instancetype)initWithRootNode:(UZNode *)rootNode 17 | startingChildNode:(UZNode *)startingChildNode 18 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 19 | extensionContext:(NSExtensionContext *)extensionContext; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /UnzipAction/UZPreviewPageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZPreviewPageViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/8/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZPreviewPageViewController.h" 10 | #import "UZPreviewViewController.h" 11 | #import "UZNode.h" 12 | #import 13 | 14 | static void * kUZPreviewIndexKey = &kUZPreviewIndexKey; 15 | 16 | @interface UZPreviewViewController (UZIndexing) 17 | @property (nonatomic, assign) NSUInteger uz_index; 18 | @end 19 | 20 | @implementation UZPreviewViewController (UZIndexing) 21 | 22 | - (NSUInteger)uz_index 23 | { 24 | return [objc_getAssociatedObject(self, kUZPreviewIndexKey) unsignedIntegerValue]; 25 | } 26 | 27 | - (void)setUz_index:(NSUInteger)uz_index 28 | { 29 | objc_setAssociatedObject(self, kUZPreviewIndexKey, @(uz_index), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 30 | } 31 | 32 | @end 33 | 34 | @interface UZPreviewPageViewController () 35 | @property (nonatomic, strong, readonly) NSExtensionContext *uz_extensionContext; 36 | @property (nonatomic, strong, readwrite) UZUnzipCoordinator *unzipCoordinator; 37 | @property (nonatomic, strong, readonly) NSArray *unencryptedChildren; 38 | @property (nonatomic, assign) NSUInteger startingIndex; 39 | @end 40 | 41 | @implementation UZPreviewPageViewController 42 | @synthesize uz_extensionContext = _uz_extensionContext; 43 | 44 | #pragma mark - Lifecycle 45 | 46 | - (instancetype)initWithRootNode:(UZNode *)rootNode 47 | startingChildNode:(UZNode *)startingChildNode 48 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 49 | extensionContext:(NSExtensionContext *)extensionContext 50 | { 51 | if ((self = [super initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil])) { 52 | self.dataSource = self; 53 | self.delegate = self; 54 | 55 | _uz_extensionContext = extensionContext; 56 | _unzipCoordinator = unzipCoordinator; 57 | _unencryptedChildren = [rootNode.children filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UZNode *child, NSDictionary *bindings) { 58 | return !child.encrypted; 59 | }]]; 60 | _startingIndex = [_unencryptedChildren indexOfObject:startingChildNode]; 61 | 62 | UZPreviewViewController *viewController = [self previewViewControllerForNodeAtIndex:_startingIndex]; 63 | [self setViewControllers:@[viewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; 64 | } 65 | return self; 66 | } 67 | 68 | - (void)viewDidLoad 69 | { 70 | [super viewDidLoad]; 71 | if (self.uz_extensionContext != nil) { 72 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Close", nil) style:UIBarButtonItemStylePlain target:self action:@selector(close)]; 73 | } 74 | } 75 | 76 | #pragma mark - UIPageViewControllerDataSource 77 | 78 | - (UZPreviewViewController *)previewViewControllerForNodeAtIndex:(NSUInteger)index 79 | { 80 | UZPreviewViewController *viewController = [[UZPreviewViewController alloc] initWithNode:self.unencryptedChildren[index] password:nil unzipCoordinator:self.unzipCoordinator extensionContext:self.uz_extensionContext]; 81 | viewController.uz_index = index; 82 | return viewController; 83 | } 84 | 85 | - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UZPreviewViewController *)viewController 86 | { 87 | return [self previewViewControllerForNodeAtIndex:viewController.uz_index - 1]; 88 | } 89 | 90 | - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UZPreviewViewController *)viewController 91 | { 92 | return [self previewViewControllerForNodeAtIndex:viewController.uz_index + 1]; 93 | } 94 | 95 | #pragma mark - Extension Context 96 | 97 | - (NSExtensionContext *)uz_extensionContext 98 | { 99 | return self.extensionContext ?: _uz_extensionContext; 100 | } 101 | 102 | + (NSSet *)keyPathsForValuesAffectingUz_extensionContext 103 | { 104 | return [NSSet setWithObject:@"extensionContext"]; 105 | } 106 | 107 | - (void)close 108 | { 109 | [self.uz_extensionContext completeRequestReturningItems:self.uz_extensionContext.inputItems completionHandler:nil]; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /UnzipAction/UZPreviewViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZPreviewViewController.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZExtensionViewController.h" 10 | 11 | @class UZNode; 12 | @class UZUnzipCoordinator; 13 | 14 | @interface UZPreviewViewController : UZExtensionViewController 15 | 16 | - (instancetype)initWithNode:(UZNode *)node 17 | password:(NSString *)password 18 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 19 | extensionContext:(NSExtensionContext *)extensionContext; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /UnzipAction/UZPreviewViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZPreviewViewController.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/5/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZPreviewViewController.h" 10 | #import "UZNode.h" 11 | #import "UZUnzipCoordinator.h" 12 | #import "UZOpenInActivity.h" 13 | 14 | #import 15 | 16 | @interface UZQuickLookPreviewItem : NSObject 17 | @property (nonatomic, strong, readonly) NSURL *fileURL; 18 | @end 19 | 20 | @interface UZPreviewViewController () 21 | @property (nonatomic, copy, readonly) NSString *password; 22 | @property (nonatomic, strong, readonly) UZNode *node; 23 | @property (nonatomic, strong, readonly) UZUnzipCoordinator *unzipCoordinator; 24 | @property (nonatomic, strong) UZUnzipOperationToken *unzipToken; 25 | @property (nonatomic, strong) UZQuickLookPreviewItem *previewItem; 26 | @property (nonatomic, strong) QLPreviewController *previewController; 27 | 28 | @property (nonatomic, weak) IBOutlet UIView *containerView; 29 | @property (nonatomic, weak) IBOutlet UILabel *progressLabel; 30 | @property (nonatomic, weak) IBOutlet UIProgressView *progressView; 31 | @end 32 | 33 | @implementation UZQuickLookPreviewItem 34 | 35 | - (instancetype)initWithFileURL:(NSURL *)fileURL 36 | { 37 | if ((self = [super init])) { 38 | _fileURL = fileURL; 39 | } 40 | return self; 41 | } 42 | 43 | #pragma mark - QLPreviewItem 44 | 45 | - (NSURL *)previewItemURL 46 | { 47 | return self.fileURL; 48 | } 49 | 50 | @end 51 | 52 | @implementation UZPreviewViewController 53 | 54 | #pragma mark - Lifecycle 55 | 56 | - (instancetype)initWithNode:(UZNode *)node 57 | password:(NSString *)password 58 | unzipCoordinator:(UZUnzipCoordinator *)unzipCoordinator 59 | extensionContext:(NSExtensionContext *)extensionContext 60 | { 61 | if ((self = [super initWithExtensionContext:extensionContext])) { 62 | _node = node; 63 | _password = [password copy]; 64 | _unzipCoordinator = unzipCoordinator; 65 | 66 | self.navigationItem.title = node.fileName; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)viewDidLoad 72 | { 73 | [super viewDidLoad]; 74 | self.progressLabel.text = self.node.fileName; 75 | } 76 | 77 | - (void)viewWillAppear:(BOOL)animated 78 | { 79 | [super viewWillAppear:animated]; 80 | if (self.previewController != nil) return; 81 | 82 | self.unzipToken = [self.unzipCoordinator unzipNode:self.node password:self.password progressHandler:^(float progress) { 83 | dispatch_async(dispatch_get_main_queue(), ^{ 84 | self.progressView.progress = progress; 85 | }); 86 | } completionHandler:^(NSURL *fileURL, NSError *error) { 87 | dispatch_async(dispatch_get_main_queue(), ^{ 88 | if (error == nil) { 89 | [self showQuickLookPreviewWithURL:fileURL]; 90 | } else { 91 | NSLog(@"%@", error); 92 | } 93 | }); 94 | }]; 95 | } 96 | 97 | - (void)viewDidDisappear:(BOOL)animated 98 | { 99 | [super viewDidDisappear:animated]; 100 | [self.unzipCoordinator cancelUnzipOperationWithToken:self.unzipToken]; 101 | self.unzipToken = nil; 102 | } 103 | 104 | - (void)showQuickLookPreviewWithURL:(NSURL *)fileURL 105 | { 106 | self.previewItem = [[UZQuickLookPreviewItem alloc] initWithFileURL:fileURL]; 107 | self.previewController = [[QLPreviewController alloc] init]; 108 | self.previewController.dataSource = self; 109 | [self.previewController reloadData]; 110 | } 111 | 112 | #pragma mark - Actions 113 | 114 | - (IBAction)performAction:(id)sender 115 | { 116 | if (self.previewItem == nil) return; 117 | 118 | UZOpenInActivity *openInActivity = [[UZOpenInActivity alloc] initWithBarButtonItem:sender]; 119 | UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[self.previewItem.fileURL] applicationActivities:@[openInActivity]]; 120 | [self presentViewController:activityViewController animated:YES completion:nil]; 121 | } 122 | 123 | #pragma mark - Accessors 124 | 125 | - (void)setPreviewController:(QLPreviewController *)previewController 126 | { 127 | if (_previewController != previewController) { 128 | [_previewController willMoveToParentViewController:nil]; 129 | [_previewController.view removeFromSuperview]; 130 | [_previewController removeFromParentViewController]; 131 | 132 | _previewController = previewController; 133 | 134 | UIView *previewView = _previewController.view; 135 | previewView.translatesAutoresizingMaskIntoConstraints = NO; 136 | 137 | [self addChildViewController:_previewController]; 138 | 139 | [self.containerView addSubview:previewView]; 140 | NSDictionary *views = NSDictionaryOfVariableBindings(previewView); 141 | [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[previewView]|" options:0 metrics:nil views:views]]; 142 | [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[previewView]|" options:0 metrics:nil views:views]]; 143 | 144 | [_previewController didMoveToParentViewController:self]; 145 | } 146 | } 147 | 148 | #pragma mark - QLPreviewControllerDataSource 149 | 150 | - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller 151 | { 152 | return 1; 153 | } 154 | 155 | - (id)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index 156 | { 157 | return self.previewItem; 158 | } 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /UnzipAction/UZPreviewViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /UnzipAction/UZUnzipCoordinator.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZUnzipCoordinator.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ZZArchive; 12 | @class UZNode; 13 | 14 | @interface UZUnzipOperationToken : NSObject 15 | @end 16 | 17 | @interface UZUnzipCoordinator : NSObject 18 | 19 | - (instancetype)initWithArchive:(ZZArchive *)archive; 20 | 21 | - (UZUnzipOperationToken *)unzipNode:(UZNode *)node 22 | password:(NSString *)password 23 | progressHandler:(void (^)(float progress))progressHandler 24 | completionHandler:(void (^)(NSURL *fileURL, NSError *error))completionHandler; 25 | 26 | - (void)cancelUnzipOperationWithToken:(UZUnzipOperationToken *)token; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /UnzipAction/UZUnzipCoordinator.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZUnzipCoordinator.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZUnzipCoordinator.h" 10 | #import "UZUnzipOperation.h" 11 | #import "UZNode.h" 12 | 13 | @interface UZUnzipCoordinator () 14 | @property (nonatomic, strong, readonly) ZZArchive *archive; 15 | @property (nonatomic, strong, readonly) NSURL *temporaryDirectoryURL; 16 | @property (nonatomic, strong, readonly) NSMutableDictionary *unarchivedURLs; 17 | @property (nonatomic, strong, readonly) NSMutableDictionary *operationSubscribers; 18 | @property (nonatomic, strong, readonly) NSMutableSet *nodesForInProgressOperations; 19 | @property (nonatomic, strong, readonly) NSOperationQueue *operationQueue; 20 | @property (nonatomic, readonly) dispatch_queue_t stateQueue; 21 | @end 22 | 23 | @interface UZUnzipOperationSubscriber : NSObject 24 | @property (nonatomic, copy, readonly) void (^progressHandler)(float); 25 | @property (nonatomic, copy, readonly) void (^completionHandler)(NSURL *, NSError *); 26 | @end 27 | 28 | @implementation UZUnzipOperationSubscriber 29 | 30 | - (instancetype)initWithProgressHandler:(void (^)(float progress))progressHandler completionHandler:(void (^)(NSURL *fileURL, NSError *error))completionHandler 31 | { 32 | if ((self = [super init])) { 33 | _progressHandler = [progressHandler copy]; 34 | _completionHandler = [completionHandler copy]; 35 | } 36 | return self; 37 | } 38 | 39 | @end 40 | 41 | @interface UZUnzipOperationToken () 42 | @property (nonatomic, strong, readonly) UZNode *node; 43 | @property (nonatomic, strong, readonly) UZUnzipOperationSubscriber *subscriber; 44 | @end 45 | 46 | @implementation UZUnzipOperationToken 47 | 48 | - (instancetype)initWithNode:(UZNode *)node subscriber:(UZUnzipOperationSubscriber *)subscriber 49 | { 50 | if ((self = [super init])) { 51 | _node = node; 52 | _subscriber = subscriber; 53 | } 54 | return self; 55 | } 56 | 57 | @end 58 | 59 | @implementation UZUnzipCoordinator 60 | 61 | - (instancetype)initWithArchive:(ZZArchive *)archive 62 | { 63 | if ((self = [super init])) { 64 | _archive = archive; 65 | _temporaryDirectoryURL = [[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES] URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]]; 66 | _unarchivedURLs = [[NSMutableDictionary alloc] init]; 67 | _operationSubscribers = [[NSMutableDictionary alloc] init]; 68 | _nodesForInProgressOperations = [[NSMutableSet alloc] init]; 69 | _operationQueue = [[NSOperationQueue alloc] init]; 70 | _stateQueue = dispatch_queue_create("com.indragie.UZUnzipCoordinator.StateQueue", DISPATCH_QUEUE_CONCURRENT); 71 | } 72 | return self; 73 | } 74 | 75 | - (void)dealloc 76 | { 77 | [[NSFileManager defaultManager] removeItemAtURL:_temporaryDirectoryURL error:nil]; 78 | } 79 | 80 | - (UZUnzipOperationToken *)unzipNode:(UZNode *)node 81 | password:(NSString *)password 82 | progressHandler:(void (^)(float progress))progressHandler 83 | completionHandler:(void (^)(NSURL *fileURL, NSError *error))completionHandler 84 | { 85 | NSURL *fileURL = [self fileURLForNode:node]; 86 | if (fileURL != nil) { 87 | if (completionHandler) { 88 | completionHandler(fileURL, nil); 89 | } 90 | return nil; 91 | } 92 | 93 | UZUnzipOperationSubscriber *subscriber = [[UZUnzipOperationSubscriber alloc] initWithProgressHandler:progressHandler completionHandler:completionHandler]; 94 | [self addSubscriber:subscriber forNode:node]; 95 | 96 | UZUnzipOperationToken *token = [[UZUnzipOperationToken alloc] initWithNode:node subscriber:subscriber]; 97 | if ([self isOperationInProgressForNode:node]) return token; 98 | 99 | UZUnzipOperation *operation = [[UZUnzipOperation alloc] initWithNode:node password:password temporaryDirectoryURL:self.temporaryDirectoryURL]; 100 | 101 | operation.progressHandler = ^(float progress) { 102 | NSArray *subscribers = [self subscribersForNode:node]; 103 | for (UZUnzipOperationSubscriber *subscriber in subscribers) { 104 | if (subscriber.progressHandler) { 105 | subscriber.progressHandler(progress); 106 | } 107 | } 108 | }; 109 | 110 | __weak UZUnzipOperation *weakOperation = operation; 111 | operation.completionBlock = ^{ 112 | NSArray *subscribers = [self subscribersForNode:node]; 113 | for (UZUnzipOperationSubscriber *subscriber in subscribers) { 114 | if (subscriber.completionHandler) { 115 | subscriber.completionHandler(weakOperation.fileURL, weakOperation.error); 116 | } 117 | } 118 | [self setFileURL:weakOperation.fileURL forNode:node]; 119 | [self setOperationInProgress:NO forNode:node]; 120 | }; 121 | 122 | [self.operationQueue addOperation:operation]; 123 | [self setOperationInProgress:YES forNode:node]; 124 | 125 | return token; 126 | } 127 | 128 | - (void)cancelUnzipOperationWithToken:(UZUnzipOperationToken *)token 129 | { 130 | if (token == nil) return; 131 | 132 | [self removeSubscriber:token.subscriber forNode:token.node]; 133 | if ([self subscribersForNode:token.node].count == 0) { 134 | for (UZUnzipOperation *operation in self.operationQueue.operations) { 135 | if ([operation.node isEqual:token.node]) { 136 | [operation cancel]; 137 | } 138 | } 139 | } 140 | } 141 | 142 | #pragma mark - Private 143 | 144 | - (void)setFileURL:(NSURL *)URL forNode:(UZNode *)node 145 | { 146 | dispatch_barrier_async(self.stateQueue, ^{ 147 | self.unarchivedURLs[node.fileName] = URL; 148 | }); 149 | } 150 | 151 | - (NSURL *)fileURLForNode:(UZNode *)node 152 | { 153 | __block NSURL *URL = nil; 154 | dispatch_sync(self.stateQueue, ^{ 155 | URL = self.unarchivedURLs[node.fileName]; 156 | }); 157 | return URL; 158 | } 159 | 160 | - (void)addSubscriber:(UZUnzipOperationSubscriber *)subscriber forNode:(UZNode *)node 161 | { 162 | dispatch_barrier_async(self.stateQueue, ^{ 163 | NSMutableArray *subscribers = self.operationSubscribers[node.fileName]; 164 | if (subscribers == nil) { 165 | subscribers = [[NSMutableArray alloc] init]; 166 | self.operationSubscribers[node.fileName] = subscribers; 167 | } 168 | [subscribers addObject:subscriber]; 169 | }); 170 | } 171 | 172 | - (NSArray *)subscribersForNode:(UZNode *)node 173 | { 174 | __block NSArray *subscribers = nil; 175 | dispatch_sync(self.stateQueue, ^{ 176 | subscribers = self.operationSubscribers[node.fileName]; 177 | }); 178 | return subscribers ?: @[]; 179 | } 180 | 181 | - (void)removeAllSubscribersForNode:(UZNode *)node 182 | { 183 | dispatch_barrier_async(self.stateQueue, ^{ 184 | [self.operationSubscribers removeObjectForKey:node.fileName]; 185 | }); 186 | } 187 | 188 | - (void)removeSubscriber:(UZUnzipOperationSubscriber *)subscriber forNode:(UZNode *)node 189 | { 190 | dispatch_barrier_async(self.stateQueue, ^{ 191 | NSMutableArray *subscribers = self.operationSubscribers[node.fileName]; 192 | [subscribers removeObject:subscriber]; 193 | }); 194 | } 195 | 196 | - (void)setOperationInProgress:(BOOL)operationInProgress forNode:(UZNode *)node 197 | { 198 | dispatch_barrier_async(self.stateQueue, ^{ 199 | if (operationInProgress) { 200 | [self.nodesForInProgressOperations addObject:node]; 201 | } else { 202 | [self.nodesForInProgressOperations removeObject:node]; 203 | } 204 | }); 205 | } 206 | 207 | - (BOOL)isOperationInProgressForNode:(UZNode *)node 208 | { 209 | __block BOOL operationInProgress = NO; 210 | dispatch_sync(self.stateQueue, ^{ 211 | operationInProgress = [self.nodesForInProgressOperations containsObject:node]; 212 | }); 213 | return operationInProgress; 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /UnzipAction/UZUnzipOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // UZUnzipOperation.h 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UZNode; 12 | 13 | extern NSString * const UZUnzipOperationErrorDomain; 14 | 15 | extern const NSInteger UZUnzipOperationErrorCodeFailedToOpen; 16 | extern const NSInteger UZUnzipOperationErrorCodeFailedToWrite; 17 | 18 | @interface UZUnzipOperation : NSOperation 19 | 20 | @property (nonatomic, strong, readonly) UZNode *node; 21 | @property (nonatomic, strong, readonly) NSError *error; 22 | @property (nonatomic, strong, readonly) NSURL *fileURL; 23 | @property (nonatomic, copy) void (^progressHandler)(float progress); 24 | 25 | - (instancetype)initWithNode:(UZNode *)node 26 | password:(NSString *)password 27 | temporaryDirectoryURL:(NSURL *)temporaryDirectoryURL; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /UnzipAction/UZUnzipOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // UZUnzipOperation.m 3 | // Unzip 4 | // 5 | // Created by Indragie on 8/6/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "UZUnzipOperation.h" 10 | #import "UZNode.h" 11 | #import 12 | 13 | NSString * const UZUnzipOperationErrorDomain = @"UZUnzipOperationErrorDomain"; 14 | const NSInteger UZUnzipOperationErrorCodeFailedToOpen = 1; 15 | const NSInteger UZUnzipOperationErrorCodeFailedToWrite = 2; 16 | 17 | @interface UZUnzipOperation () 18 | @property (nonatomic, copy, readwrite) NSString *password; 19 | @property (nonatomic, strong, readonly) NSInputStream *stream; 20 | @property (nonatomic, strong, readonly) NSURL *temporaryDirectoryURL; 21 | @property (nonatomic, strong, readwrite) NSError *error; 22 | @property (nonatomic, strong, readwrite) NSURL *fileURL; 23 | @end 24 | 25 | @implementation UZUnzipOperation 26 | 27 | - (instancetype)initWithNode:(UZNode *)node 28 | password:(NSString *)password 29 | temporaryDirectoryURL:(NSURL *)temporaryDirectoryURL 30 | { 31 | NSParameterAssert(node); 32 | NSParameterAssert(temporaryDirectoryURL); 33 | 34 | if ((self = [super init])) { 35 | _node = node; 36 | _password = [password copy]; 37 | _temporaryDirectoryURL = temporaryDirectoryURL; 38 | 39 | NSError *error = nil; 40 | NSInputStream *stream = [self.node streamWithPassword:_password error:&error]; 41 | if (stream == nil) { 42 | self.error = error; 43 | } else { 44 | _stream = stream; 45 | } 46 | } 47 | return self; 48 | } 49 | 50 | - (void)main 51 | { 52 | if (self.error != nil) return; 53 | 54 | @autoreleasepool { 55 | NSURL *subdirectoryURL = [self.temporaryDirectoryURL URLByAppendingPathComponent:[[NSUUID UUID] UUIDString] isDirectory:YES]; 56 | NSURL *fileURL = [subdirectoryURL URLByAppendingPathComponent:self.node.fileName isDirectory:NO]; 57 | 58 | NSFileManager *fm = [[NSFileManager alloc] init]; 59 | NSError *error = nil; 60 | if (![fm createDirectoryAtURL:subdirectoryURL withIntermediateDirectories:YES attributes:nil error:&error]) { 61 | self.error = error; 62 | return; 63 | } 64 | 65 | FILE *fd = fopen(fileURL.path.UTF8String, "w"); 66 | @onExit { 67 | fclose(fd); 68 | }; 69 | 70 | if (fd == NULL) { 71 | self.error = [NSError errorWithDomain:UZUnzipOperationErrorDomain code:UZUnzipOperationErrorCodeFailedToOpen userInfo:nil]; 72 | return; 73 | } 74 | 75 | [self.stream open]; 76 | @onExit { 77 | [self.stream close]; 78 | }; 79 | 80 | NSUInteger totalBytesRead = 0; 81 | const NSUInteger totalSize = self.node.uncompressedSize; 82 | 83 | while (totalBytesRead < totalSize && !self.cancelled) { 84 | const NSUInteger remainingBytes = totalSize - totalBytesRead; 85 | uint8_t *bytes = malloc(sizeof(uint8_t) * remainingBytes); 86 | @onExit { 87 | free(bytes); 88 | }; 89 | 90 | NSInteger bytesRead = [self.stream read:bytes maxLength:remainingBytes]; 91 | 92 | if (bytesRead > 0) { 93 | if (fwrite(bytes, sizeof(uint8_t), bytesRead, fd) == bytesRead) { 94 | totalBytesRead += bytesRead; 95 | 96 | if (self.progressHandler != nil) { 97 | self.progressHandler((float)totalBytesRead / totalSize); 98 | } 99 | } else { 100 | self.error = [NSError errorWithDomain:UZUnzipOperationErrorDomain code:UZUnzipOperationErrorCodeFailedToWrite userInfo:nil]; 101 | break; 102 | } 103 | } else { 104 | self.error = self.stream.streamError; 105 | break; 106 | } 107 | } 108 | 109 | if (self.error == nil && !self.cancelled) { 110 | self.fileURL = fileURL; 111 | } else { 112 | [fm removeItemAtURL:fileURL error:nil]; 113 | } 114 | } 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /UnzipAction/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indragiek/Unzip/8cac33e893a87a72b3e30ef82b522de23884a3af/UnzipAction/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indragiek/Unzip/8cac33e893a87a72b3e30ef82b522de23884a3af/demo.gif --------------------------------------------------------------------------------