├── tags.sketch ├── johhny-logo.sketch ├── Johnny ├── johnny-logo.png ├── Extensions │ ├── watchOS │ │ └── WatchKit+Johnny.swift │ ├── UIButton+Johnny.swift │ ├── UIColor+Johnny.swift │ ├── UIImage+Johnny.swift │ ├── macOS │ │ └── AppKit+Johnny.swift │ ├── UIImageView+Johnny.swift │ └── common │ │ └── Foundation+Johnny.swift ├── Log.swift ├── Johnny.h ├── Info.plist ├── Memory.swift ├── Johnny.swift └── Disk.swift ├── Johnny tvOS ├── tvOS.modulemap ├── Johnny tvOS.h └── Info.plist ├── Johnny macOS ├── macOS.modulemap ├── Johnny macOS.h └── Info.plist ├── Johnny watchOS ├── watchOS.modulemap ├── Johnny watchOS.h └── Info.plist ├── Johnny.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── Johnny tvOS.xcscheme │ │ ├── Johnny macOS.xcscheme │ │ ├── Johnny watchOS.xcscheme │ │ └── Johnny iOS.xcscheme └── project.pbxproj ├── .gitignore ├── JohnnyTests ├── Info.plist ├── JohnnyMemoryTests.swift └── JohnnyDiskTests.swift ├── LICENSE ├── Johnny.podspec ├── pod.svg ├── license.svg ├── language.svg ├── platform.svg └── README.md /tags.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zolomatok/Johnny/HEAD/tags.sketch -------------------------------------------------------------------------------- /johhny-logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zolomatok/Johnny/HEAD/johhny-logo.sketch -------------------------------------------------------------------------------- /Johnny/johnny-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zolomatok/Johnny/HEAD/Johnny/johnny-logo.png -------------------------------------------------------------------------------- /Johnny tvOS/tvOS.modulemap: -------------------------------------------------------------------------------- 1 | framework module Johnny { 2 | umbrella header "Johnny tvOS.h" 3 | 4 | export * 5 | module * { export * } 6 | } -------------------------------------------------------------------------------- /Johnny macOS/macOS.modulemap: -------------------------------------------------------------------------------- 1 | framework module Johnny { 2 | umbrella header "Johnny macOS.h" 3 | 4 | export * 5 | module * { export * } 6 | } -------------------------------------------------------------------------------- /Johnny watchOS/watchOS.modulemap: -------------------------------------------------------------------------------- 1 | framework module Johnny { 2 | umbrella header "Johnny watchOS.h" 3 | 4 | export * 5 | module * { export * } 6 | } -------------------------------------------------------------------------------- /Johnny.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Johnny/Extensions/watchOS/WatchKit+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WatchKit+Johnny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 7/4/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | //import WatchKit 11 | -------------------------------------------------------------------------------- /Johnny.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Johnny/Log.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Log.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/27/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Log { 12 | 13 | fileprivate static let Tag = "[JOHNNY]" 14 | static func log(_ message: String, _ error: Error) { 15 | #if DEBUG 16 | NSLog(Tag + " " + message + ":" + (error as NSError).description) 17 | #endif 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Johnny/Extensions/UIButton+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+Johnny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIButton { 12 | 13 | open func imageWithURL(_ url: String?, placeholder: UIImage? = nil, completion: ((_ image: UIImage?)->Void)? = nil) { 14 | self.imageView?.imageWithURL(url, placeholder: placeholder, completion: completion) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | # Swift Package Manager 30 | .build/ 31 | 32 | # CocoaPods 33 | Pods/ -------------------------------------------------------------------------------- /Johnny/Johnny.h: -------------------------------------------------------------------------------- 1 | // 2 | // Johnny.h 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Johnny. 12 | FOUNDATION_EXPORT double JohnnyVersionNumber; 13 | 14 | //! Project version string for Johnny. 15 | FOUNDATION_EXPORT const unsigned char JohnnyVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Johnny tvOS/Johnny tvOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Johnny tvOS.h 3 | // Johnny tvOS 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Johnny tvOS. 12 | FOUNDATION_EXPORT double Johnny_tvOSVersionNumber; 13 | 14 | //! Project version string for Johnny tvOS. 15 | FOUNDATION_EXPORT const unsigned char Johnny_tvOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Johnny macOS/Johnny macOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Johnny macOS.h 3 | // Johnny macOS 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Johnny macOS. 12 | FOUNDATION_EXPORT double Johnny_macOSVersionNumber; 13 | 14 | //! Project version string for Johnny macOS. 15 | FOUNDATION_EXPORT const unsigned char Johnny_macOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Johnny watchOS/Johnny watchOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Johnny watchOS.h 3 | // Johnny watchOS 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Johnny watchOS. 12 | FOUNDATION_EXPORT double Johnny_watchOSVersionNumber; 13 | 14 | //! Project version string for Johnny watchOS. 15 | FOUNDATION_EXPORT const unsigned char Johnny_watchOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /JohnnyTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Johnny/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Johnny tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Johnny watchOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Johnny/Memory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Memory.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/26/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | class Memory: NSCache { 13 | 14 | override init() { 15 | super.init() 16 | #if UIKIT_COMPATIBLE && !WATCHKIT 17 | NotificationCenter.default.addObserver(self, selector: #selector(removeAllObjects), name: .UIApplicationDidReceiveMemoryWarning, object: nil) 18 | #endif 19 | } 20 | 21 | subscript(key: String) -> AnyObject? { 22 | 23 | get { 24 | return self.object(forKey: key as AnyObject) 25 | } 26 | 27 | set(newValue) { 28 | if let newValue = newValue { 29 | self.setObject(newValue, forKey: key as AnyObject) 30 | } else { 31 | self.removeObject(forKey: key as AnyObject) 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Johnny/Extensions/UIColor+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension-UIColor.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class Color: Codable { 12 | let colorString: String 13 | public init?(color: UIColor?) { 14 | guard let color = color else { return nil } 15 | guard let components = color.cgColor.components else { return nil } 16 | colorString = "\(components[0]), \(components[1]), \(components[2]), \(components[3])" 17 | } 18 | 19 | open func uiColor() -> UIColor { 20 | let components = colorString.components(separatedBy: ", ") 21 | return UIColor(red: CGFloat((components[0] as NSString).floatValue), 22 | green: CGFloat((components[1] as NSString).floatValue), 23 | blue: CGFloat((components[2] as NSString).floatValue), 24 | alpha: CGFloat((components[3] as NSString).floatValue)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Johnny macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 Zoltán Matók. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Zoltán Matók 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Johnny.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Johnny" 3 | s.version = "3.0.5" 4 | s.summary = "Melodic Caching in Swift" 5 | s.homepage = "https://github.com/zolomatok/Johnny" 6 | s.license = { :type => "MIT" } 7 | s.authors = { "Zoltán Matók" => "bellamycpt@gmail.com" } 8 | 9 | s.source = { :git => "https://github.com/zolomatok/Johnny.git", :tag => "3.0.5"} 10 | s.requires_arc = true 11 | s.module_name = 'Johnny' 12 | s.swift_version = '4.0' 13 | 14 | s.osx.deployment_target = "10.10" 15 | s.ios.deployment_target = "8.0" 16 | s.tvos.deployment_target = "9.0" 17 | s.watchos.deployment_target = "2.0" 18 | 19 | s.osx.source_files = 'Johnny/*.swift', 'Johnny/Extensions/common/*.swift', 'Johnny/Extensions/macOS/*.swift' 20 | s.ios.source_files = 'Johnny/*.swift', 'Johnny/Extensions/*.swift', 'Johnny/Extensions/common/*.swift' 21 | s.tvos.source_files = 'Johnny/*.swift', 'Johnny/Extensions/*.swift', 'Johnny/Extensions/common/*.swift' 22 | s.watchos.source_files = 'Johnny/*.swift', 'Johnny/Extensions/common/*.swift', 'Johnny/Extensions/UIColor.swift', 'Johnny/Extensions/UIImage.swift' 23 | 24 | s.osx.frameworks = 'AppKit', 'Foundation' 25 | s.ios.frameworks = 'UIKit', 'Foundation' 26 | s.tvos.frameworks = 'UIKit', 'Foundation' 27 | end 28 | -------------------------------------------------------------------------------- /Johnny/Extensions/UIImage+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Johhny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/30/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | #if WATCHKIT 11 | import WatchKit 12 | #endif 13 | 14 | // See: https://github.com/AFNetworking/AFNetworking/issues/2572#issuecomment-115854482 15 | private let imgLock = NSLock() 16 | open class Image: Codable { 17 | let data: Data 18 | public init?(image: UIImage?) { 19 | guard let image = image else { return nil } 20 | data = UIImageJPEGRepresentation(image, 1.0)! 21 | } 22 | 23 | open func uiImage() -> UIImage { 24 | imgLock.lock() 25 | let img = UIImage(data: data)! 26 | imgLock.unlock() 27 | return img 28 | } 29 | } 30 | 31 | 32 | extension UIImage { 33 | 34 | func hasAlpha() -> Bool { 35 | 36 | guard let alpha = self.cgImage?.alphaInfo else { 37 | return false 38 | } 39 | 40 | switch alpha { 41 | case .first, .last, .premultipliedFirst, .premultipliedLast, .alphaOnly: 42 | return true 43 | case .none, .noneSkipFirst, .noneSkipLast: 44 | return false 45 | } 46 | } 47 | 48 | 49 | func scaledToSize(_ toSize: CGSize) -> UIImage { 50 | UIGraphicsBeginImageContextWithOptions(toSize, !hasAlpha(), 0.0) 51 | draw(in: CGRect(x: 0, y: 0, width: toSize.width, height: toSize.height)) 52 | let resizedImage = UIGraphicsGetImageFromCurrentImageContext() 53 | UIGraphicsEndImageContext() 54 | return resizedImage! 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pod.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pod 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | pod 29 | 30 | 31 | pod 32 | 33 | 34 | v1.0 35 | 36 | 37 | v1.0 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /license.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | license 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | license 29 | 30 | 31 | license 32 | 33 | 34 | MIT 35 | 36 | 37 | MIT 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /language.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | language 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | language 29 | 30 | 31 | language 32 | 33 | 34 | swift2 35 | 36 | 37 | swift2 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Johnny/Extensions/macOS/AppKit+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppKit+Johnny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 7/4/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | 13 | // See: https://github.com/AFNetworking/AFNetworking/issues/2572#issuecomment-115854482 14 | private let imgLock = NSLock() 15 | class Image: Codable { 16 | let data: Data 17 | init?(image: NSImage?) { 18 | guard let image = image else { return nil } 19 | guard let d = image.tiffRepresentation else { return nil } 20 | data = d 21 | } 22 | 23 | func nsImage() -> NSImage { 24 | imgLock.lock() 25 | let img = NSImage(data: data)! 26 | imgLock.unlock() 27 | return img 28 | } 29 | } 30 | 31 | 32 | extension NSImageView { 33 | 34 | open func imageWithURL(_ url: String?, placeholder: NSImage? = nil, completion: ((_ image: NSImage?)->Void)? = nil) { 35 | 36 | // Handle nil 37 | guard let url = url else { 38 | completion?(nil) 39 | return 40 | } 41 | 42 | 43 | // Set placeholder 44 | if let placeholder = placeholder { 45 | self.image = placeholder 46 | } 47 | 48 | 49 | // Get cache 50 | Johnny.pull(url) { (cached: Image?) in 51 | 52 | 53 | // Return cached value 54 | if let cached = cached { 55 | if completion == nil { self.image = cached.nsImage() } 56 | else { completion?(cached.nsImage()) } 57 | return 58 | } 59 | 60 | 61 | // Download from URL 62 | var img: NSImage? 63 | DispatchQueue.global(qos: .background).async { 64 | let data = try? Data(contentsOf: URL(string: url)!) 65 | if let data = data { 66 | img = NSImage(data: data) 67 | } 68 | 69 | DispatchQueue.main.async { 70 | // Save to cache & return 71 | if img != nil { Johnny.cache(Image(image: img), key: url) } 72 | if completion == nil { 73 | self.image = img 74 | } 75 | else { completion?(img) } 76 | 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /platform.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | platform 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | platform 30 | 31 | 32 | platform 33 | 34 | 35 | iOS 8.0 | macOS 10.10 | tvOS 9.0 | watchOS 2.0 36 | 37 | 38 | iOS 8.0 | macOS 10.10 | tvOS 9.0 | watchOS 2.0 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Johnny/Extensions/UIImageView+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Johnny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/27/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImageView { 12 | 13 | open func imageWithURL(_ url: String?, placeholder: UIImage? = nil, completion: ((_ image: UIImage?)->Void)? = nil) { 14 | 15 | // Handle nil 16 | guard let url = url else { 17 | completion?(nil) 18 | return 19 | } 20 | 21 | 22 | // Set placeholder 23 | if let placeholder = placeholder { 24 | self.image = placeholder 25 | } 26 | 27 | 28 | // Get cache 29 | Johnny.pull(url) { (cached: Image?) in 30 | 31 | 32 | // Return cached value 33 | if let cached = cached { 34 | if completion == nil { self.image = cached.uiImage() } 35 | else { completion?(cached.uiImage()) } 36 | return 37 | } 38 | 39 | 40 | // Download from URL 41 | var img: UIImage? 42 | DispatchQueue.global(qos: .background).async { 43 | let data = try? Data(contentsOf: URL(string: url)!) 44 | if let data = data { 45 | img = UIImage(data: data) 46 | // img = self.resizeImage(img) 47 | } 48 | 49 | DispatchQueue.main.async { 50 | // Save to cache & return 51 | if let img = img { Johnny.cache(Image(image: img), key: url) } 52 | if completion == nil { 53 | self.image = img 54 | } 55 | else { completion?(img) } 56 | } 57 | } 58 | } 59 | } 60 | 61 | 62 | fileprivate func resizeImage(_ img: UIImage?) -> UIImage? { 63 | 64 | guard let img = img else { return nil } 65 | 66 | 67 | // Get target size 68 | var targetSize: CGSize 69 | switch self.contentMode { 70 | case .scaleToFill: 71 | targetSize = bounds.size 72 | case .scaleAspectFit: 73 | targetSize = img.size.aspectFitSize(bounds.size) 74 | case .scaleAspectFill: 75 | targetSize = img.size.aspectFillSize(bounds.size) 76 | default: 77 | return img 78 | } 79 | assert(bounds.size.width > 0 && bounds.size.height > 0, "Expected non-zero size.") 80 | 81 | 82 | // Avoid unnecessary computations 83 | if (targetSize.width == img.size.width && targetSize.height == img.size.height) { 84 | return img 85 | } 86 | 87 | 88 | // Resize 89 | let resizedImage = img.scaledToSize(targetSize) 90 | return resizedImage 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Johnny.xcodeproj/xcshareddata/xcschemes/Johnny tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Johnny.xcodeproj/xcshareddata/xcschemes/Johnny macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Johnny.xcodeproj/xcshareddata/xcschemes/Johnny watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Johnny/Extensions/common/Foundation+Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Foundation+Johnny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 7/4/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | 12 | protocol Cachable: Codable {} 13 | 14 | extension CGSize { 15 | 16 | func aspectFillSize(_ size: CGSize) -> CGSize { 17 | let scaleWidth = size.width / self.width 18 | let scaleHeight = size.height / self.height 19 | let scale = max(scaleWidth, scaleHeight) 20 | 21 | let resultSize = CGSize(width: self.width * scale, height: self.height * scale) 22 | return CGSize(width: ceil(resultSize.width), height: ceil(resultSize.height)) 23 | } 24 | 25 | func aspectFitSize(_ size: CGSize) -> CGSize { 26 | let targetAspect = size.width / size.height 27 | let sourceAspect = self.width / self.height 28 | var resultSize = size 29 | 30 | if (targetAspect > sourceAspect) { 31 | resultSize.width = size.height * sourceAspect 32 | } 33 | else { 34 | resultSize.height = size.width / sourceAspect 35 | } 36 | return CGSize(width: ceil(resultSize.width), height: ceil(resultSize.height)) 37 | } 38 | } 39 | 40 | 41 | extension FileManager { 42 | 43 | func enumerateContentsOfDirectoryAtPath(_ path: String, orderedByProperty property: String, ascending: Bool, usingBlock block: (URL, Int, inout Bool) -> Void ) { 44 | 45 | let directoryURL = URL(fileURLWithPath: path) 46 | do { 47 | let contents = try self.contentsOfDirectory(at: directoryURL, includingPropertiesForKeys: [URLResourceKey(rawValue: property)], options: FileManager.DirectoryEnumerationOptions()) 48 | let sortedContents = contents.sorted(by: {(URL1: URL, URL2: URL) -> Bool in 49 | 50 | // Maybe there's a better way to do this. See: http://stackoverflow.com/questions/25502914/comparing-anyobject-in-swift 51 | 52 | var value1 : AnyObject? 53 | do { 54 | try (URL1 as NSURL).getResourceValue(&value1, forKey: URLResourceKey(rawValue: property)); 55 | } catch { 56 | return true 57 | } 58 | var value2 : AnyObject? 59 | do { 60 | try (URL2 as NSURL).getResourceValue(&value2, forKey: URLResourceKey(rawValue: property)); 61 | } catch { 62 | return false 63 | } 64 | 65 | if let string1 = value1 as? String, let string2 = value2 as? String { 66 | return ascending ? string1 < string2 : string2 < string1 67 | } 68 | 69 | if let date1 = value1 as? Date, let date2 = value2 as? Date { 70 | return ascending ? date1 < date2 : date2 < date1 71 | } 72 | 73 | if let number1 = value1 as? NSNumber, let number2 = value2 as? NSNumber { 74 | return ascending ? number1 < number2 : number2 < number1 75 | } 76 | 77 | return false 78 | }) 79 | 80 | for (i, v) in sortedContents.enumerated() { 81 | var stop : Bool = false 82 | block(v, i, &stop) 83 | if stop { break } 84 | } 85 | 86 | } catch { 87 | print("Failed to list directory", error as NSError) 88 | } 89 | } 90 | 91 | } 92 | 93 | func < (lhs: NSNumber, rhs: NSNumber) -> Bool { 94 | return lhs.compare(rhs) == ComparisonResult.orderedAscending 95 | } 96 | -------------------------------------------------------------------------------- /Johnny/Johnny.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Johnny.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/26/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if UIKIT_COMPATIBLE 11 | import UIKit 12 | #endif 13 | 14 | 15 | // Used to add T to NSCache 16 | class Shell : NSObject { 17 | let value: Any? 18 | init(value: Any?) { 19 | self.value = value 20 | } 21 | } 22 | 23 | 24 | 25 | open class Johnny { 26 | 27 | static let memory = Memory() 28 | static let disk = Disk() 29 | 30 | 31 | // MARK: - In 32 | /** 33 | Caches a Storable object 34 | 35 | - parameter value: The value to be cached 36 | - parameter key: The key for pulling the object from the cache 37 | */ 38 | open class func cache(_ value: T?, key: String, library: Bool = false) { 39 | guard let value = value else { return } 40 | 41 | // Store in memory 42 | memory[key] = Shell(value: value) 43 | 44 | // Store in disk 45 | DispatchQueue.global(qos: .background).async { 46 | let d = try? JSONEncoder().encode(value) 47 | guard let data = d else { return } 48 | disk[key, library] = data 49 | } 50 | } 51 | 52 | 53 | // MARK: - Delete 54 | /** 55 | Delete a Storable object 56 | 57 | - parameter key: The key for deleting the object from the cache 58 | */ 59 | open class func delete(_ key: String, library: Bool = false) { 60 | memory[key] = nil 61 | disk[key, library] = nil 62 | } 63 | 64 | 65 | 66 | // MARK: - Out 67 | /** 68 | Retrieves a Storable object for a given key 69 | 70 | - parameter key: The key for pulling the object from the cache 71 | 72 | - returns: The stored object, nil if not found 73 | */ 74 | open class func pull(_ key: String, library: Bool = false) -> T? { 75 | 76 | // Check memory 77 | if let value = (memory[key] as? Shell)?.value as? T { return value } 78 | 79 | 80 | // Check disk 81 | if let data = disk[key, library] { 82 | 83 | let value = try? JSONDecoder().decode(T.self, from: data) 84 | if let value = value { memory[key] = Shell(value: value) } 85 | return value 86 | } 87 | 88 | return nil 89 | } 90 | 91 | 92 | // MARK: - Async Out 93 | /** 94 | Asyncronously retrieves a Storable object for a given key 95 | 96 | - parameter key: The key for pulling the object from the cache 97 | 98 | - returns: The stored object, nil if not found 99 | */ 100 | open class func pull(_ key: String, library: Bool = false, completion: ((_ value: T?)->Void)? = nil ) { 101 | 102 | // Check memory 103 | if let value = (memory[key] as? Shell)?.value as? T { 104 | completion?(value) 105 | return 106 | } 107 | 108 | 109 | // Check disk 110 | var value: T? 111 | DispatchQueue.global(qos: .background).async { 112 | let data = disk[key, library] 113 | if data != nil { 114 | value = try? JSONDecoder().decode(T.self, from: data!) 115 | } 116 | 117 | DispatchQueue.main.async { 118 | guard let value = value else { completion?(nil); return } 119 | 120 | memory[key] = Shell(value: value) 121 | completion?(value) 122 | } 123 | } 124 | } 125 | 126 | 127 | 128 | // MARK: - Clear 129 | /** 130 | Nukes the entire cache from memory & disk 131 | */ 132 | @objc open class func nuke() { 133 | memory.removeAllObjects() 134 | disk.nuke(nil) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Johnny.xcodeproj/xcshareddata/xcschemes/Johnny iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](/Johnny/johnny-logo.png?raw=true) 2 | 3 | ![platform](https://cdn.rawgit.com/zolomatok/Johnny/master/platform.svg) 4 | ![license](https://cdn.rawgit.com/zolomatok/Johnny/master/license.svg) 5 | 6 | Johnny is a generic caching library written for Swift 4. 7 | 8 | ## Features 9 | **Johnny can cache any model object that conforms to the `Cachable` protocol.** 10 | 11 | - [x] Out-of-the-box support: 12 | - String, Bool, Int, UInt, Int64, UInt64, Float, Double 13 | - URL, Data, Date 14 | - UIImage, UIColor **by wrapping them in an Image or Color wrapper class respectively** 15 | - Arrays, Dictionaries and Sets of the above 16 | - [x] Multiplatform, supporting iOS, macOS, tvOS & watchOS 17 | - [x] First-level memory cache using `NSCache` 18 | - [x] Second-level LRU disk cache 19 | - [x] Disk access in background thread (always when saving, optionally when fetching) 20 | - [x] Syncronous & Asynchronous API support 21 | - [x] Automatic cache eviction on memory warnings & disk capacity reached 22 | - [x] Unit tested 23 | - [x] Concise, well-structured code to encourage contributions 24 | 25 | Extra ❤️ for images: 26 | - [x] `func setImageWithURL` extension on UIImageView, UIButton & NSImageView, optimized for cell reuse 27 | 28 | ## Usage 29 | 30 | ### Caching ### 31 | ```swift 32 | Johnny.cache(user, key: "LocalUser") 33 | 34 | let imgage = UIImage(named: "awesomeness") 35 | Johnny.cache(Image(image), key: "Image") // Caching a UIImage. UIColor objects must be wrapped in the Color wrapper class the same way. 36 | 37 | // You can flag a value to be stored in the Library instead of the Caches folder if you don't want it to be automatically purged: 38 | Johnny.cache(Date(), key: "FirstStart", library: true) 39 | ``` 40 | 41 | ### Pulling ### 42 | 43 | ```swift 44 | // The type of the retrived value must be explicitly stated for the compiler. 45 | let date: Date? = Johnny.pull("FirstStart") 46 | 47 | // If you know you are retrieving a large object (> 1.5 MB) you can do it asynchronously 48 | Johnny.pull("4KImage") { (image: Image?) in 49 | let img = image.uiImage() 50 | } 51 | ``` 52 | 53 | ### Removing ### 54 | ```swift 55 | Johnny.remove("LocalUser") 56 | ``` 57 | 58 | 59 | ## Examples 60 | 61 | ### Collections ### 62 | 63 | You can cache any collection of items conforming to the Storable protocol (most standard library data types already do) 64 | 65 | ```swift 66 | let array: [String] = ["Folsom", "Prison", "Blues"] 67 | let stringSet: Set = ["I've", "been", "everywhere"] 68 | // In case of dictionaries, the value must explicitly conform to Storable (so [String: AnyObject] does not work, while [String: Double] does) 69 | let dictionary: [String: String] = ["first": "Solitary", "second": "man"] 70 | 71 | Johnny.cache(array, key: "folsom") 72 | Johnny.cache(stringSet, key: "everywhere") 73 | Johnny.cache(dictionary, key: "solitary") 74 | ``` 75 | 76 | ### Custom types ### 77 | 78 | Due to current Swift limitations, since the Storable protocol has an `associatedType`, conformance must be added through an extension. 79 | `class User: Storable` will not work. 80 | 81 | 82 | ```swift 83 | struct User { 84 | 85 | enum Badassery: String { case Total } 86 | 87 | var name: String? 88 | var uid: Int 89 | var badassery: Badassery 90 | } 91 | 92 | 93 | extension User: Storable { 94 | typealias Result = User 95 | 96 | static func fromData(data: NSData) -> User.Result? { 97 | let dict = try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! [NSObject: AnyObject] 98 | 99 | let user = User() 100 | user.uid = dict["identification"] as! Int 101 | user.name = dict["name"] as? String 102 | user.badassery = Badassery(rawValue: dict["badassery"] as! String)! 103 | return user 104 | } 105 | 106 | func toData() -> NSData { 107 | let json = ["identification": uid, "name": name, "badassery": badassery.rawValue] 108 | return try! NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions()) 109 | } 110 | } 111 | ``` 112 | 113 | **Using it with Johnny:** 114 | 115 | 116 | ```swift 117 | let lily = User(name: "Lily", uid: 84823682, badassery: .Total) 118 | Johnny.cache(lily, key: "Lily") 119 | 120 | let cachedLily: User = Johnny.pull("Lily") 121 | ``` 122 | 123 | ## Requirements 124 | - iOS 8.0+ 125 | - macOS 10.10+ 126 | - tvOS 9.0+ 127 | - watchOS 2.0+ 128 | - Swift 4.0 129 | 130 | ## Install 131 | 132 | **CocoaPods** 133 | 134 | ``` 135 | pod 'Johnny' 136 | ``` 137 | 138 | **Carthage** 139 | 140 | ``` 141 | github "zolomatok/Johnny" 142 | ``` 143 | 144 | **Manual** 145 | - Clone the project 146 | - Select the scheme (platform) and build 147 | - Drag Johnny.framework to your project 148 | - In the project settings under your target's General tab, scroll down and add Johhny to the ```Embedded Binaries``` section if it's not already added. 149 | 150 | 151 | ## Attribution 152 | I'd like to thank the creators of [Pantry](https://github.com/nickoneill/Pantry) and [Haneke](https://github.com/Haneke/HanekeSwift) as those projects provided much of the inspiration and some code. Johnny was dreamed up to be the best of both worlds. 153 | 154 | ## License 155 | Johnny is released under the MIT license. See LICENSE for details. 156 | -------------------------------------------------------------------------------- /Johnny/Disk.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Disk.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 6/26/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Disk { 12 | 13 | fileprivate let cachePath = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("io.johhny") 14 | fileprivate let libraryPath = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("io.johhny") 15 | fileprivate var size: UInt64 = 0 16 | fileprivate var capacity = UINT64_MAX 17 | fileprivate var diskQueue = DispatchQueue(label: "io.johhny.disk") 18 | 19 | 20 | // MARK: - Init 21 | init() { 22 | createDirectory() 23 | 24 | diskQueue.async { 25 | self.calculateSize() 26 | } 27 | 28 | #if UIKIT_COMPATIBLE && !WATCHKIT 29 | NotificationCenter.default.addObserver(self, selector: #selector(nuke), name: .UIApplicationDidReceiveMemoryWarning, object: nil) 30 | #endif 31 | } 32 | 33 | 34 | // MARK: - Operations 35 | subscript(key: String) -> Data? { 36 | get { return get(key) } 37 | set(newValue) { 38 | diskQueue.async { 39 | if newValue != nil { self.add(key, newValue: newValue!) } 40 | else { self.remove(key) } 41 | } 42 | } 43 | } 44 | 45 | subscript(key: String, library: Bool) -> Data? { 46 | get { return get(key, library: library) } 47 | set(newValue) { 48 | diskQueue.async { 49 | if newValue != nil { self.add(key, newValue: newValue!, library: library) } 50 | else { self.remove(key, library: library) } 51 | } 52 | } 53 | } 54 | 55 | 56 | fileprivate func get(_ key: String, library: Bool = false) -> Data? { 57 | let path = getPath(key, library: library) 58 | let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: Data.ReadingOptions()) 59 | updateAccessDate(path) 60 | return data 61 | } 62 | 63 | 64 | fileprivate func add(_ key: String, newValue: Data, library: Bool = false) { 65 | createDirectory() 66 | 67 | let path = getPath(key, library: library) 68 | let pastAttributes = try? FileManager.default.attributesOfItem(atPath: path) as NSDictionary 69 | do { 70 | try newValue.write(to: URL(fileURLWithPath: path), options: .atomicWrite) 71 | if let attributes = pastAttributes { 72 | self.substractSize(attributes.fileSize()) 73 | } 74 | size += UInt64(newValue.count) 75 | controlSize() 76 | } catch { 77 | if isNoSuchFileError(error as NSError) { Log.log("Failed to write key \(key)", error) } 78 | } 79 | } 80 | 81 | fileprivate func remove(_ key: String, library: Bool = false) { 82 | 83 | // Only proceed if file exists, otherwise, the whole containing dir will be removed 84 | guard FileManager.default.fileExists(atPath: getPath(key, library: library), isDirectory: nil) == true else { 85 | return 86 | } 87 | 88 | do { 89 | let attributes : NSDictionary = try FileManager.default.attributesOfItem(atPath: library ? libraryPath : cachePath) as NSDictionary 90 | let fileSize = attributes.fileSize() 91 | print("JOHNNY REMOVING PATH: ", getPath(key, library: library)) 92 | try FileManager.default.removeItem(atPath: getPath(key, library: library)) 93 | substractSize(fileSize) 94 | } catch { 95 | if isNoSuchFileError(error as NSError) { Log.log("File remove error", error) } 96 | } 97 | } 98 | 99 | 100 | @objc func nuke(libraryToo: Bool = false, _ completion: (()->Void)? = nil) { 101 | diskQueue.async { 102 | 103 | if libraryToo { 104 | let contents = try? FileManager.default.contentsOfDirectory(atPath: self.libraryPath) 105 | contents?.forEach({ (fileName) in 106 | let filePath = (self.libraryPath as NSString).appendingPathComponent(fileName) 107 | _ = try? FileManager.default.removeItem(atPath: filePath) 108 | }) 109 | } 110 | 111 | let contents = try? FileManager.default.contentsOfDirectory(atPath: self.cachePath) 112 | contents?.forEach({ (fileName) in 113 | let filePath = (self.cachePath as NSString).appendingPathComponent(fileName) 114 | _ = try? FileManager.default.removeItem(atPath: filePath) 115 | }) 116 | 117 | self.calculateSize() 118 | 119 | DispatchQueue.main.async { 120 | completion?() 121 | } 122 | } 123 | } 124 | 125 | 126 | 127 | // MARK: - Maintenance 128 | func getPath(_ key: String, library: Bool = false) -> String { 129 | let escapedFilename = [ "\0":"%00", ":":"%3A", "/":"%2F" ] 130 | .reduce(key.components(separatedBy: "%").joined(separator: "%25")) { 131 | str, m in str.components(separatedBy: m.0).joined(separator: m.1) } 132 | return ((library ? libraryPath : cachePath) as NSString).appendingPathComponent(escapedFilename) 133 | } 134 | 135 | fileprivate func createDirectory() { 136 | 137 | // Cache 138 | var isDir: ObjCBool = false 139 | let exists = FileManager.default.fileExists(atPath: cachePath, isDirectory: &isDir) 140 | if !exists || !isDir.boolValue { 141 | try! FileManager.default.createDirectory(atPath: cachePath, withIntermediateDirectories: true, attributes: nil) 142 | } 143 | 144 | // Library 145 | var isDirLib: ObjCBool = false 146 | let existsLib = FileManager.default.fileExists(atPath: libraryPath, isDirectory: &isDirLib) 147 | if !existsLib || !isDirLib.boolValue { 148 | try! FileManager.default.createDirectory(atPath: libraryPath, withIntermediateDirectories: true, attributes: nil) 149 | } 150 | } 151 | 152 | fileprivate func calculateSize() { 153 | size = 0 154 | 155 | do { 156 | let contents = try FileManager.default.contentsOfDirectory(atPath: cachePath) 157 | for fileName in contents { 158 | let p = (cachePath as NSString).appendingPathComponent(fileName) 159 | do { 160 | let attributes : NSDictionary = try FileManager.default.attributesOfItem(atPath: p) as NSDictionary 161 | size += attributes.fileSize() 162 | } catch { 163 | if self.isNoSuchFileError(error as NSError) { Log.log("Failed to read file size of \(p)", error) } 164 | } 165 | } 166 | } catch { 167 | if self.isNoSuchFileError(error as NSError) { Log.log("Failed to list directory", error) } 168 | } 169 | } 170 | 171 | 172 | fileprivate func controlSize() { 173 | 174 | if size <= capacity { return } 175 | 176 | let fileManager = FileManager.default 177 | fileManager.enumerateContentsOfDirectoryAtPath(cachePath, orderedByProperty: URLResourceKey.contentModificationDateKey.rawValue, ascending: true) { (URL : Foundation.URL, _, stop : inout Bool) -> Void in 178 | 179 | self[(URL.path as NSString).deletingLastPathComponent] = nil 180 | stop = self.size <= self.capacity 181 | } 182 | } 183 | 184 | 185 | fileprivate func substractSize(_ s : UInt64) { 186 | size = size >= s ? size-s : 0 187 | } 188 | 189 | 190 | fileprivate func updateAccessDate(_ path: String) { 191 | diskQueue.async { 192 | let now = Date() 193 | do { 194 | try FileManager.default.setAttributes([FileAttributeKey.modificationDate : now], ofItemAtPath: path) 195 | } catch { 196 | if self.isNoSuchFileError(error as NSError) { Log.log("Failed to update access date", error) } 197 | } 198 | } 199 | } 200 | 201 | 202 | fileprivate func isNoSuchFileError(_ error : NSError?) -> Bool { 203 | if let error = error { 204 | return NSCocoaErrorDomain == error.domain && error.code == NSFileReadNoSuchFileError 205 | } 206 | return false 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /JohnnyTests/JohnnyMemoryTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JohnnyTests.swift 3 | // JohnnyTests 4 | // 5 | // Created by Zolo on 7/27/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Johnny 11 | 12 | class JohnnyMemoryTests: XCTestCase { 13 | 14 | private lazy var __once: () = { 15 | let cachesPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] 16 | let johhny = "io.johhny" 17 | let path = (cachesPath as NSString).appendingPathComponent(johhny) 18 | let paths = try? FileManager.default.contentsOfDirectory(atPath: path) 19 | if let paths = paths { 20 | for p in paths { 21 | let _ = try? FileManager.default.removeItem(atPath: p) 22 | } 23 | } 24 | }() 25 | 26 | override func setUp() { 27 | super.setUp() 28 | _ = self.__once 29 | } 30 | 31 | override func tearDown() { super.tearDown() } 32 | 33 | 34 | func testDefaultTypes() { 35 | let string: String = "Heresy grows from idleness" 36 | let bool: Bool = true 37 | let int: Int = 1 38 | let int64: Int64 = 2 39 | let uint: UInt = 3 40 | let uint64: UInt64 = 4 41 | let float: Float = 10.2 42 | let double: Double = 20.6 43 | 44 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 45 | let image = Image(image: UIImage(contentsOfFile: ip)!) 46 | let data = image!.data 47 | let date: Date = Date(timeIntervalSince1970: 1459355217) 48 | let color: Color = Color(color: UIColor.blue)! 49 | 50 | 51 | Johnny.cache(string, key: "testString") 52 | Johnny.cache(bool, key: "testBool") 53 | Johnny.cache(int, key: "testInt") 54 | Johnny.cache(int64, key: "testInt64") 55 | Johnny.cache(uint, key: "testUInt") 56 | Johnny.cache(uint64, key: "testUInt64") 57 | Johnny.cache(float, key: "testFloat") 58 | Johnny.cache(double, key: "testDouble") 59 | Johnny.cache(image, key: "testImage") 60 | Johnny.cache(data, key: "testData") 61 | Johnny.cache(date, key: "testDate") 62 | Johnny.cache(color, key: "testColor") 63 | 64 | 65 | if let value: String = Johnny.pull("testString") { 66 | XCTAssert(value == "Heresy grows from idleness", "default string was incorrect") 67 | } else { 68 | XCTFail("no default string could be unpacked") 69 | } 70 | 71 | if let value: Bool = Johnny.pull("testBool") { 72 | XCTAssert(value == true, "default bool was incorrect") 73 | } else { 74 | XCTFail("no default bool could be unpacked") 75 | } 76 | 77 | if let value: Int = Johnny.pull("testInt") { 78 | XCTAssert(value == 1, "default int was incorrect") 79 | } else { 80 | XCTFail("no default int could be unpacked") 81 | } 82 | 83 | if let value: Int64 = Johnny.pull("testInt64") { 84 | XCTAssert(value == 2, "default int64 was incorrect") 85 | } else { 86 | XCTFail("no default int64 could be unpacked") 87 | } 88 | 89 | if let value: UInt = Johnny.pull("testUInt") { 90 | XCTAssert(value == 3, "default uint was incorrect") 91 | } else { 92 | XCTFail("no default uint could be unpacked") 93 | } 94 | 95 | if let value: UInt64 = Johnny.pull("testUInt64") { 96 | XCTAssert(value == 4, "default uint64 was incorrect") 97 | } else { 98 | XCTFail("no default uint64 could be unpacked") 99 | } 100 | 101 | if let value: Float = Johnny.pull("testFloat") { 102 | XCTAssert(value == 10.2, "default float was incorrect") 103 | } else { 104 | XCTFail("no default float could be unpacked") 105 | } 106 | 107 | if let value: Double = Johnny.pull("testDouble") { 108 | XCTAssert(value == 20.6, "default Double was incorrect") 109 | } else { 110 | XCTFail("no default Double could be unpacked") 111 | } 112 | 113 | if let value: Image = Johnny.pull("testImage") { 114 | XCTAssert(value.data == data, "default Image was incorrect") 115 | } else { 116 | XCTFail("no default Image could be unpacked") 117 | } 118 | 119 | if let value: Data = Johnny.pull("testData") { 120 | XCTAssert(value == data, "default Data was incorrect") 121 | } else { 122 | XCTFail("no default Data could be unpacked") 123 | } 124 | 125 | if let value: Date = Johnny.pull("testDate") { 126 | XCTAssert(value.timeIntervalSince1970 == 1459355217 , "default Date was incorrect") 127 | } else { 128 | XCTFail("no default Date could be unpacked") 129 | } 130 | 131 | if let value: Color = Johnny.pull("testColor") { 132 | XCTAssert(value.uiColor() == UIColor.blue, "default Color was incorrect") 133 | } else { 134 | XCTFail("no default Color could be unpacked") 135 | } 136 | } 137 | 138 | 139 | func testOptionalDefaultTypes() { 140 | let string: String? = "Heresy grows from idleness" 141 | let int: Int? = 1 142 | let int64: Int64? = 2 143 | let uint: UInt? = 3 144 | let uint64: UInt64? = 4 145 | let float: Float? = 10.2 146 | let double: Double? = 20.6 147 | 148 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 149 | let image = Image(image: UIImage(contentsOfFile: ip)) 150 | let data: Data? = image?.data 151 | let date: Date? = Date(timeIntervalSince1970: 1459355217) 152 | let color: Color? = Color(color: UIColor.blue) 153 | 154 | 155 | Johnny.cache(string, key: "testStringOptional") 156 | Johnny.cache(int, key: "testIntOptional") 157 | Johnny.cache(int64, key: "testInt64Optional") 158 | Johnny.cache(uint, key: "testUIntOptional") 159 | Johnny.cache(uint64, key: "testUInt64Optional") 160 | Johnny.cache(float, key: "testFloatOptional") 161 | Johnny.cache(double, key: "testDoubleOptional") 162 | Johnny.cache(image, key: "testImageOptional") 163 | Johnny.cache(data, key: "testDataOptional") 164 | Johnny.cache(date, key: "testDateOptional") 165 | Johnny.cache(color, key: "testColorOptional") 166 | 167 | 168 | if let value: String = Johnny.pull("testStringOptional") { 169 | XCTAssert(value == "Heresy grows from idleness", "default optional string was incorrect") 170 | } else { 171 | XCTFail("no default optional string could be unpacked") 172 | } 173 | 174 | if let value: Int = Johnny.pull("testIntOptional") { 175 | XCTAssert(value == 1, "default optional int was incorrect") 176 | } else { 177 | XCTFail("no default optional int could be unpacked") 178 | } 179 | 180 | if let value: Int64 = Johnny.pull("testInt64Optional") { 181 | XCTAssert(value == 2, "default optional int64 was incorrect") 182 | } else { 183 | XCTFail("no default optional int64 could be unpacked") 184 | } 185 | 186 | if let value: UInt = Johnny.pull("testUIntOptional") { 187 | XCTAssert(value == 3, "default optional uint was incorrect") 188 | } else { 189 | XCTFail("no default optional uint could be unpacked") 190 | } 191 | 192 | if let value: UInt64 = Johnny.pull("testUInt64Optional") { 193 | XCTAssert(value == 4, "default optional uint64 was incorrect") 194 | } else { 195 | XCTFail("no default optional uint64 could be unpacked") 196 | } 197 | 198 | if let value: Float = Johnny.pull("testFloatOptional") { 199 | XCTAssert(value == 10.2, "default optional float was incorrect") 200 | } else { 201 | XCTFail("no default optional float could be unpacked") 202 | } 203 | 204 | if let value: Double = Johnny.pull("testDoubleOptional") { 205 | XCTAssert(value == 20.6, "default optional Double was incorrect") 206 | } else { 207 | XCTFail("no default optional Double could be unpacked") 208 | } 209 | 210 | if let value: Image = Johnny.pull("testImageOptional") { 211 | XCTAssert(value.data == data, "default optional Image was incorrect") 212 | } else { 213 | XCTFail("no default optional Image could be unpacked") 214 | } 215 | 216 | if let value: Data = Johnny.pull("testDataOptional") { 217 | XCTAssert(value == data, "default optional Data was incorrect") 218 | } else { 219 | XCTFail("no default optional Data could be unpacked") 220 | } 221 | 222 | if let value: Date = Johnny.pull("testDateOptional") { 223 | XCTAssert(value.timeIntervalSince1970 == 1459355217 , "default optional Date was incorrect") 224 | } else { 225 | XCTFail("no default optional Date could be unpacked") 226 | } 227 | 228 | if let value: Color = Johnny.pull("testColorOptional") { 229 | XCTAssert(value.uiColor() == UIColor.blue, "default optional Color was incorrect") 230 | } else { 231 | XCTFail("no default optional Color could be unpacked") 232 | } 233 | } 234 | 235 | 236 | func testCollections() { 237 | 238 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 239 | let image = Image(image: UIImage(contentsOfFile: ip)!) 240 | let data = image!.data 241 | 242 | let stringArray: [String] = ["Heresy", "grows", "from", "idleness"] 243 | let intArray: [Int] = [1,2] 244 | let dataArray: [Data] = [data] 245 | 246 | let stringMap: [String: String] = ["first": "Heresy", "second": "grows"] 247 | let intMap: [String: Int] = ["first": 1, "second": 2] 248 | let dataMap: [String: Data] = ["first": data] 249 | 250 | let stringSet: Set = ["Heresy", "grows", "from", "idleness"] 251 | let intSet: Set = [1,2] 252 | let dataSet: Set = [data] 253 | 254 | 255 | Johnny.cache(stringArray, key: "testStringArray") 256 | Johnny.cache(intArray, key: "testIntArray") 257 | Johnny.cache(dataArray, key: "testDataArray") 258 | Johnny.cache(stringMap, key: "testStringMap") 259 | Johnny.cache(intMap, key: "testIntMap") 260 | Johnny.cache(dataMap, key: "testDataMap") 261 | Johnny.cache(stringSet, key: "testStringSet") 262 | Johnny.cache(intSet, key: "testIntSet") 263 | Johnny.cache(dataSet, key: "testDataSet") 264 | 265 | 266 | if let value: [String] = Johnny.pull("testStringArray") { 267 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default string array was incorrect") 268 | } else { 269 | XCTFail("no default string array could be unpacked") 270 | } 271 | 272 | if let value: [Int] = Johnny.pull("testIntArray") { 273 | XCTAssert(value == [1,2], "default int array was incorrect") 274 | } else { 275 | XCTFail("no default int array could be unpacked") 276 | } 277 | 278 | if let value: [Data] = Johnny.pull("testDataArray") { 279 | XCTAssert(value == [data], "default int array was incorrect") 280 | } else { 281 | XCTFail("no default int array could be unpacked") 282 | } 283 | 284 | if let value: [String: String] = Johnny.pull("testStringMap") { 285 | XCTAssert(value == ["first": "Heresy", "second": "grows"], "default string map was incorrect") 286 | } else { 287 | XCTFail("no default string map could be unpacked") 288 | } 289 | 290 | if let value: [String: Int] = Johnny.pull("testIntMap") { 291 | XCTAssert(value == ["first": 1, "second": 2], "default int map was incorrect") 292 | } else { 293 | XCTFail("no default int map could be unpacked") 294 | } 295 | 296 | if let value: [String: Data] = Johnny.pull("testDataMap") { 297 | XCTAssert(value == ["first": data], "default data map was incorrect") 298 | } else { 299 | XCTFail("no default data map could be unpacked") 300 | } 301 | 302 | if let value: Set = Johnny.pull("testStringSet") { 303 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default string set was incorrect") 304 | } else { 305 | XCTFail("no default string set could be unpacked") 306 | } 307 | 308 | if let value: Set = Johnny.pull("testIntSet") { 309 | XCTAssert(value == [1,2], "default int set was incorrect") 310 | } else { 311 | XCTFail("no default int set could be unpacked") 312 | } 313 | 314 | if let value: Set = Johnny.pull("testDataSet") { 315 | XCTAssert(value == [data], "default int set was incorrect") 316 | } else { 317 | XCTFail("no default int set could be unpacked") 318 | } 319 | } 320 | 321 | 322 | func testOptionalCollections() { 323 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 324 | let image = Image(image: UIImage(contentsOfFile: ip)!) 325 | let data = image!.data 326 | 327 | let stringArray: [String]? = ["Heresy", "grows", "from", "idleness"] 328 | let intArray: [Int]? = [1,2] 329 | let dataArray: [Data]? = [data] 330 | 331 | let stringMap: [String: String]? = ["first": "Heresy", "second": "grows"] 332 | let intMap: [String: Int]? = ["first": 1, "second": 2] 333 | let dataMap: [String: Data]? = ["first": data] 334 | 335 | let stringSet: Set? = ["Heresy", "grows", "from", "idleness"] 336 | let intSet: Set? = [1,2] 337 | let dataSet: Set? = [data] 338 | 339 | 340 | Johnny.cache(stringArray, key: "testStringArrayOptional") 341 | Johnny.cache(intArray, key: "testIntArrayOptional") 342 | Johnny.cache(dataArray, key: "testDataArrayOptional") 343 | Johnny.cache(stringMap, key: "testStringMapOptional") 344 | Johnny.cache(intMap, key: "testIntMapOptional") 345 | Johnny.cache(dataMap, key: "testDataMapOptional") 346 | Johnny.cache(stringSet, key: "testStringSetOptional") 347 | Johnny.cache(intSet, key: "testIntSetOptional") 348 | Johnny.cache(dataSet, key: "testDataSetOptional") 349 | 350 | 351 | if let value: [String] = Johnny.pull("testStringArrayOptional") { 352 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default optional string array was incorrect") 353 | } else { 354 | XCTFail("no default optional string array could be unpacked") 355 | } 356 | 357 | if let value: [Int] = Johnny.pull("testIntArrayOptional") { 358 | XCTAssert(value == [1,2], "default optional int array was incorrect") 359 | } else { 360 | XCTFail("no default optional int array could be unpacked") 361 | } 362 | 363 | if let value: [Data] = Johnny.pull("testDataArrayOptional") { 364 | XCTAssert(value == [data], "default optional int array was incorrect") 365 | } else { 366 | XCTFail("no default optional int array could be unpacked") 367 | } 368 | 369 | if let value: [String: String] = Johnny.pull("testStringMapOptional") { 370 | XCTAssert(value == ["first": "Heresy", "second": "grows"], "default optional string map was incorrect") 371 | } else { 372 | XCTFail("no default optional string map could be unpacked") 373 | } 374 | 375 | if let value: [String: Int] = Johnny.pull("testIntMapOptional") { 376 | XCTAssert(value == ["first": 1, "second": 2], "default optional int map was incorrect") 377 | } else { 378 | XCTFail("no default optional int map could be unpacked") 379 | } 380 | 381 | if let value: [String: Data] = Johnny.pull("testDataMapOptional") { 382 | XCTAssert(value == ["first": data], "default optional data map was incorrect") 383 | } else { 384 | XCTFail("no default optional data map could be unpacked") 385 | } 386 | 387 | if let value: Set = Johnny.pull("testStringSetOptional") { 388 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default optional string set was incorrect") 389 | } else { 390 | XCTFail("no default optional string set could be unpacked") 391 | } 392 | 393 | if let value: Set = Johnny.pull("testIntSetOptional") { 394 | XCTAssert(value == [1,2], "default optional int set was incorrect") 395 | } else { 396 | XCTFail("no default optional int set could be unpacked") 397 | } 398 | 399 | if let value: Set = Johnny.pull("testDataSetOptional") { 400 | XCTAssert(value == [data], "default optional int set was incorrect") 401 | } else { 402 | XCTFail("no default optional int set could be unpacked") 403 | } 404 | } 405 | 406 | 407 | func testStorables() { 408 | let spacemarine: Ultramarine = Ultramarine() 409 | Johnny.cache(spacemarine, key: "spacemarine") 410 | 411 | if let spacemarine: Ultramarine = Johnny.pull("spacemarine") { 412 | XCTAssert(spacemarine.identification == 84823682 && spacemarine.badassery == Ultramarine.Badassery.Total, "storable object was incorrect") 413 | } else { 414 | XCTFail("no Storable object could be unpacked") 415 | } 416 | 417 | // let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) 418 | // dispatch_after(delayTime, dispatch_get_main_queue()) { 419 | // Johnny.memory.removeAllObjects() 420 | // 421 | // 422 | // } 423 | } 424 | 425 | 426 | func testOptionalStorables() { 427 | let spacemarine: Ultramarine? = Ultramarine() 428 | Johnny.cache(spacemarine, key: "spacemarine") 429 | 430 | if let spacemarine: Ultramarine = Johnny.pull("spacemarine") { 431 | XCTAssert(spacemarine.identification == 84823682 && spacemarine.badassery == Ultramarine.Badassery.Total, "storable object was incorrect") 432 | } else { 433 | XCTFail("no Storable object could be unpacked") 434 | } 435 | } 436 | } 437 | 438 | 439 | class Ultramarine: Cachable { 440 | 441 | enum Badassery: String, Cachable { 442 | case Total 443 | } 444 | 445 | var identification: Int = 84823682 446 | var badassery = Badassery.Total 447 | } 448 | -------------------------------------------------------------------------------- /JohnnyTests/JohnnyDiskTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JohnnyDiskTests.swift 3 | // Johnny 4 | // 5 | // Created by Zolo on 7/27/16. 6 | // Copyright © 2016 Zoltán Matók. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import XCTest 12 | @testable import Johnny 13 | 14 | class JohnnyDiskTests: XCTestCase { 15 | 16 | private lazy var __once: () = { 17 | let cachesPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] 18 | let johhny = "io.johhny" 19 | let path = (cachesPath as NSString).appendingPathComponent(johhny) 20 | let paths = try? FileManager.default.contentsOfDirectory(atPath: path) 21 | if let paths = paths { 22 | for p in paths { 23 | let _ = try? FileManager.default.removeItem(atPath: p) 24 | } 25 | } 26 | }() 27 | 28 | var token: Int = 0 29 | var disk = Disk() 30 | 31 | override func setUp() { 32 | super.setUp() 33 | _ = self.__once 34 | } 35 | 36 | override func tearDown() { super.tearDown() } 37 | 38 | 39 | func testDefaultTypes() { 40 | let string: String = "Heresy grows from idleness" 41 | let int: Int = 1 42 | let int64: Int64 = 2 43 | let uint: UInt = 3 44 | let uint64: UInt64 = 4 45 | let float: Float = 10.2 46 | let double: Double = 20.6 47 | 48 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 49 | let image = Image(image: UIImage(contentsOfFile: ip)!) 50 | let data = image!.data 51 | let date: Date = Date(timeIntervalSince1970: 1459355217) 52 | let color = Color(color: UIColor.blue) 53 | 54 | 55 | Johnny.cache(string, key: "testString") 56 | Johnny.cache(int, key: "testInt") 57 | Johnny.cache(int64, key: "testInt64") 58 | Johnny.cache(uint, key: "testUInt") 59 | Johnny.cache(uint64, key: "testUInt64") 60 | Johnny.cache(float, key: "testFloat") 61 | Johnny.cache(double, key: "testDouble") 62 | Johnny.cache(image, key: "testImage") 63 | Johnny.cache(data, key: "testData") 64 | Johnny.cache(date, key: "testDate") 65 | Johnny.cache(color, key: "testColor") 66 | 67 | 68 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1)) 69 | 70 | 71 | if let value: String = Johnny.pull("testString") { 72 | XCTAssert(value == "Heresy grows from idleness", "default string was incorrect") 73 | } else { 74 | XCTFail("no default string could be unpacked") 75 | } 76 | 77 | if let value: Int = Johnny.pull("testInt") { 78 | XCTAssert(value == 1, "default int was incorrect") 79 | } else { 80 | XCTFail("no default int could be unpacked") 81 | } 82 | 83 | if let value: Int64 = Johnny.pull("testInt64") { 84 | XCTAssert(value == 2, "default int64 was incorrect") 85 | } else { 86 | XCTFail("no default int64 could be unpacked") 87 | } 88 | 89 | if let value: UInt = Johnny.pull("testUInt") { 90 | XCTAssert(value == 3, "default uint was incorrect") 91 | } else { 92 | XCTFail("no default uint could be unpacked") 93 | } 94 | 95 | if let value: UInt64 = Johnny.pull("testUInt64") { 96 | XCTAssert(value == 4, "default uint64 was incorrect") 97 | } else { 98 | XCTFail("no default uint64 could be unpacked") 99 | } 100 | 101 | if let value: Float = Johnny.pull("testFloat") { 102 | XCTAssert(value == 10.2, "default float was incorrect") 103 | } else { 104 | XCTFail("no default float could be unpacked") 105 | } 106 | 107 | if let value: Double = Johnny.pull("testDouble") { 108 | XCTAssert(value == 20.6, "default Double was incorrect") 109 | } else { 110 | XCTFail("no default Double could be unpacked") 111 | } 112 | 113 | if let value: Image = Johnny.pull("testImage") { 114 | XCTAssert(value.data == data, "default Image was incorrect") 115 | } else { 116 | XCTFail("no default Image could be unpacked") 117 | } 118 | 119 | if let value: Data = Johnny.pull("testData") { 120 | XCTAssert(value == data, "default Data was incorrect") 121 | } else { 122 | XCTFail("no default Data could be unpacked") 123 | } 124 | 125 | if let value: Date = Johnny.pull("testDate") { 126 | XCTAssert(value.timeIntervalSince1970 == 1459355217 , "default Date was incorrect") 127 | } else { 128 | XCTFail("no default Date could be unpacked") 129 | } 130 | 131 | if let value: Color = Johnny.pull("testColor") { 132 | XCTAssert(value.uiColor() == UIColor.blue, "default Color was incorrect") 133 | } else { 134 | XCTFail("no default Color could be unpacked") 135 | } 136 | } 137 | 138 | 139 | func testOptionalDefaultTypes() { 140 | let string: String? = "Heresy grows from idleness" 141 | let int: Int? = 1 142 | let int64: Int64? = 2 143 | let uint: UInt? = 3 144 | let uint64: UInt64? = 4 145 | let float: Float? = 10.2 146 | let double: Double? = 20.6 147 | 148 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 149 | let image = Image(image: UIImage(contentsOfFile: ip)) 150 | let data: Data? = image?.data 151 | let date: Date? = Date(timeIntervalSince1970: 1459355217) 152 | let color: Color? = Color(color: UIColor.blue) 153 | 154 | 155 | Johnny.cache(string, key: "testStringOptional") 156 | Johnny.cache(int, key: "testIntOptional") 157 | Johnny.cache(int64, key: "testInt64Optional") 158 | Johnny.cache(uint, key: "testUIntOptional") 159 | Johnny.cache(uint64, key: "testUInt64Optional") 160 | Johnny.cache(float, key: "testFloatOptional") 161 | Johnny.cache(double, key: "testDoubleOptional") 162 | Johnny.cache(image, key: "testImageOptional") 163 | Johnny.cache(data, key: "testDataOptional") 164 | Johnny.cache(date, key: "testDateOptional") 165 | Johnny.cache(color, key: "testColorOptional") 166 | 167 | 168 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1)) 169 | 170 | 171 | if let value: String = Johnny.pull("testStringOptional") { 172 | XCTAssert(value == "Heresy grows from idleness", "default optional string was incorrect") 173 | } else { 174 | XCTFail("no default optional string could be unpacked") 175 | } 176 | 177 | if let value: Int = Johnny.pull("testIntOptional") { 178 | XCTAssert(value == 1, "default optional int was incorrect") 179 | } else { 180 | XCTFail("no default optional int could be unpacked") 181 | } 182 | 183 | if let value: Int64 = Johnny.pull("testInt64Optional") { 184 | XCTAssert(value == 2, "default optional int64 was incorrect") 185 | } else { 186 | XCTFail("no default optional int64 could be unpacked") 187 | } 188 | 189 | if let value: UInt = Johnny.pull("testUIntOptional") { 190 | XCTAssert(value == 3, "default optional uint was incorrect") 191 | } else { 192 | XCTFail("no default optional uint could be unpacked") 193 | } 194 | 195 | if let value: UInt64 = Johnny.pull("testUInt64Optional") { 196 | XCTAssert(value == 4, "default optional uint64 was incorrect") 197 | } else { 198 | XCTFail("no default optional uint64 could be unpacked") 199 | } 200 | 201 | if let value: Float = Johnny.pull("testFloatOptional") { 202 | XCTAssert(value == 10.2, "default optional float was incorrect") 203 | } else { 204 | XCTFail("no default optional float could be unpacked") 205 | } 206 | 207 | if let value: Double = Johnny.pull("testDoubleOptional") { 208 | XCTAssert(value == 20.6, "default optional Double was incorrect") 209 | } else { 210 | XCTFail("no default optional Double could be unpacked") 211 | } 212 | 213 | if let value: Image = Johnny.pull("testImageOptional") { 214 | XCTAssert(value.data == data, "default optional Image was incorrect") 215 | } else { 216 | XCTFail("no default optional Image could be unpacked") 217 | } 218 | 219 | if let value: Data = Johnny.pull("testDataOptional") { 220 | XCTAssert(value == data, "default optional Data was incorrect") 221 | } else { 222 | XCTFail("no default optional Data could be unpacked") 223 | } 224 | 225 | if let value: Date = Johnny.pull("testDateOptional") { 226 | XCTAssert(value.timeIntervalSince1970 == 1459355217 , "default optional Date was incorrect") 227 | } else { 228 | XCTFail("no default optional Date could be unpacked") 229 | } 230 | 231 | if let value: Color = Johnny.pull("testColorOptional") { 232 | XCTAssert(value.uiColor() == UIColor.blue, "default optional Color was incorrect") 233 | } else { 234 | XCTFail("no default optional Color could be unpacked") 235 | } 236 | } 237 | 238 | 239 | func testCollections() { 240 | 241 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 242 | let image = Image(image: UIImage(contentsOfFile: ip)!) 243 | let data = image!.data 244 | 245 | let stringArray: [String] = ["Heresy", "grows", "from", "idleness"] 246 | let intArray: [Int] = [1,2] 247 | let dataArray: [Data] = [data] 248 | 249 | let stringMap: [String: String] = ["first": "Heresy", "second": "grows"] 250 | let intMap: [String: Int] = ["first": 1, "second": 2] 251 | let dataMap: [String: Data] = ["first": data] 252 | 253 | let stringSet: Set = ["Heresy", "grows", "from", "idleness"] 254 | let intSet: Set = [1,2] 255 | let dataSet: Set = [data] 256 | 257 | 258 | Johnny.cache(stringArray, key: "testStringArray") 259 | Johnny.cache(intArray, key: "testIntArray") 260 | Johnny.cache(dataArray, key: "testDataArray") 261 | Johnny.cache(stringMap, key: "testStringMap") 262 | Johnny.cache(intMap, key: "testIntMap") 263 | Johnny.cache(dataMap, key: "testDataMap") 264 | Johnny.cache(stringSet, key: "testStringSet") 265 | Johnny.cache(intSet, key: "testIntSet") 266 | Johnny.cache(dataSet, key: "testDataSet") 267 | 268 | 269 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1)) 270 | 271 | 272 | if let value: [String] = Johnny.pull("testStringArray") { 273 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default string array was incorrect") 274 | } else { 275 | XCTFail("no default string array could be unpacked") 276 | } 277 | 278 | if let value: [Int] = Johnny.pull("testIntArray") { 279 | XCTAssert(value == [1,2], "default int array was incorrect") 280 | } else { 281 | XCTFail("no default int array could be unpacked") 282 | } 283 | 284 | if let value: [Data] = Johnny.pull("testDataArray") { 285 | XCTAssert(value == [data], "default int array was incorrect") 286 | } else { 287 | XCTFail("no default int array could be unpacked") 288 | } 289 | 290 | if let value: [String: String] = Johnny.pull("testStringMap") { 291 | XCTAssert(value == ["first": "Heresy", "second": "grows"], "default string map was incorrect") 292 | } else { 293 | XCTFail("no default string map could be unpacked") 294 | } 295 | 296 | if let value: [String: Int] = Johnny.pull("testIntMap") { 297 | XCTAssert(value == ["first": 1, "second": 2], "default int map was incorrect") 298 | } else { 299 | XCTFail("no default int map could be unpacked") 300 | } 301 | 302 | if let value: [String: Data] = Johnny.pull("testDataMap") { 303 | XCTAssert(value == ["first": data], "default data map was incorrect") 304 | } else { 305 | XCTFail("no default data map could be unpacked") 306 | } 307 | 308 | if let value: Set = Johnny.pull("testStringSet") { 309 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default string set was incorrect") 310 | } else { 311 | XCTFail("no default string set could be unpacked") 312 | } 313 | 314 | if let value: Set = Johnny.pull("testIntSet") { 315 | XCTAssert(value == [1,2], "default int set was incorrect") 316 | } else { 317 | XCTFail("no default int set could be unpacked") 318 | } 319 | 320 | if let value: Set = Johnny.pull("testDataSet") { 321 | XCTAssert(value == [data], "default int set was incorrect") 322 | } else { 323 | XCTFail("no default int set could be unpacked") 324 | } 325 | } 326 | 327 | 328 | func testOptionalCollections() { 329 | let ip = Bundle(for: type(of: self)).resourcePath! + "/johnny-logo.png" 330 | let image = Image(image: UIImage(contentsOfFile: ip)!) 331 | let data = image!.data 332 | 333 | let stringArray: [String]? = ["Heresy", "grows", "from", "idleness"] 334 | let intArray: [Int]? = [1,2] 335 | let dataArray: [Data]? = [data] 336 | 337 | let stringMap: [String: String]? = ["first": "Heresy", "second": "grows"] 338 | let intMap: [String: Int]? = ["first": 1, "second": 2] 339 | let dataMap: [String: Data]? = ["first": data] 340 | 341 | let stringSet: Set? = ["Heresy", "grows", "from", "idleness"] 342 | let intSet: Set? = [1,2] 343 | let dataSet: Set? = [data] 344 | 345 | 346 | Johnny.cache(stringArray, key: "testStringArrayOptional") 347 | Johnny.cache(intArray, key: "testIntArrayOptional") 348 | Johnny.cache(dataArray, key: "testDataArrayOptional") 349 | Johnny.cache(stringMap, key: "testStringMapOptional") 350 | Johnny.cache(intMap, key: "testIntMapOptional") 351 | Johnny.cache(dataMap, key: "testDataMapOptional") 352 | Johnny.cache(stringSet, key: "testStringSetOptional") 353 | Johnny.cache(intSet, key: "testIntSetOptional") 354 | Johnny.cache(dataSet, key: "testDataSetOptional") 355 | 356 | 357 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1)) 358 | 359 | 360 | if let value: [String] = Johnny.pull("testStringArrayOptional") { 361 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default optional string array was incorrect") 362 | } else { 363 | XCTFail("no default optional string array could be unpacked") 364 | } 365 | 366 | if let value: [Int] = Johnny.pull("testIntArrayOptional") { 367 | XCTAssert(value == [1,2], "default optional int array was incorrect") 368 | } else { 369 | XCTFail("no default optional int array could be unpacked") 370 | } 371 | 372 | if let value: [Data] = Johnny.pull("testDataArrayOptional") { 373 | XCTAssert(value == [data], "default optional int array was incorrect") 374 | } else { 375 | XCTFail("no default optional int array could be unpacked") 376 | } 377 | 378 | if let value: [String: String] = Johnny.pull("testStringMapOptional") { 379 | XCTAssert(value == ["first": "Heresy", "second": "grows"], "default optional string map was incorrect") 380 | } else { 381 | XCTFail("no default optional string map could be unpacked") 382 | } 383 | 384 | if let value: [String: Int] = Johnny.pull("testIntMapOptional") { 385 | XCTAssert(value == ["first": 1, "second": 2], "default optional int map was incorrect") 386 | } else { 387 | XCTFail("no default optional int map could be unpacked") 388 | } 389 | 390 | if let value: [String: Data] = Johnny.pull("testDataMapOptional") { 391 | XCTAssert(value == ["first": data], "default optional data map was incorrect") 392 | } else { 393 | XCTFail("no default optional data map could be unpacked") 394 | } 395 | 396 | if let value: Set = Johnny.pull("testStringSetOptional") { 397 | XCTAssert(value == ["Heresy", "grows", "from", "idleness"], "default optional string set was incorrect") 398 | } else { 399 | XCTFail("no default optional string set could be unpacked") 400 | } 401 | 402 | if let value: Set = Johnny.pull("testIntSetOptional") { 403 | XCTAssert(value == [1,2], "default optional int set was incorrect") 404 | } else { 405 | XCTFail("no default optional int set could be unpacked") 406 | } 407 | 408 | if let value: Set = Johnny.pull("testDataSetOptional") { 409 | XCTAssert(value == [data], "default optional int set was incorrect") 410 | } else { 411 | XCTFail("no default optional int set could be unpacked") 412 | } 413 | } 414 | 415 | 416 | func testStorables() { 417 | let spacemarine: Ultramarine = Ultramarine() 418 | Johnny.cache(spacemarine, key: "spacemarine") 419 | 420 | 421 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1)) 422 | 423 | 424 | if let spacemarine: Ultramarine = Johnny.pull("spacemarine") { 425 | XCTAssert(spacemarine.identification == 84823682 && spacemarine.badassery == Ultramarine.Badassery.Total, "storable object was incorrect") 426 | } else { 427 | XCTFail("no Storable object could be unpacked") 428 | } 429 | } 430 | 431 | 432 | func testOptionalStorables() { 433 | let spacemarine: Ultramarine? = Ultramarine() 434 | Johnny.cache(spacemarine, key: "spacemarine") 435 | 436 | 437 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1)) 438 | 439 | 440 | if let spacemarine: Ultramarine = Johnny.pull("spacemarine") { 441 | XCTAssert(spacemarine.identification == 84823682 && spacemarine.badassery == Ultramarine.Badassery.Total, "storable object was incorrect") 442 | } else { 443 | XCTFail("no Storable object could be unpacked") 444 | } 445 | } 446 | 447 | func testDeletion() { 448 | let key = "testString" 449 | Johnny.cache("Heresy grows from idleness", key: key) 450 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 451 | Johnny.delete(key) 452 | if let _: String = Johnny.pull(key) { 453 | XCTFail("no Storable object could be unpacked") 454 | } else { 455 | XCTAssert(true) 456 | } 457 | } 458 | } 459 | 460 | func testLibrary() { 461 | let x = expectation(description: "Object was stored in the Library") 462 | let key = "testString" 463 | disk[key, true] = "And we shall no know fear".data(using: .utf8) 464 | 465 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 466 | self.disk.nuke() 467 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 468 | XCTAssert(self.disk[key, true] != nil, "Object was not stored in the library") 469 | x.fulfill() 470 | } 471 | } 472 | 473 | waitForExpectations(timeout: 3) { (e) in } 474 | } 475 | 476 | func testNuke() { 477 | let key = "testString" 478 | disk[key, true] = "And we shall no know fear".data(using: .utf8) 479 | disk.nuke(libraryToo: true) 480 | XCTAssert(disk[key] == nil, "Nuke didn't clear the library") 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /Johnny.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F3036D361D2584A80056BCE4 /* UIButton+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB051D25634F005B30AB /* UIButton+Johnny.swift */; }; 11 | F3036D371D2584B00056BCE4 /* UIImage+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB061D25634F005B30AB /* UIImage+Johnny.swift */; }; 12 | F3036D381D2584C30056BCE4 /* UIImage+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB061D25634F005B30AB /* UIImage+Johnny.swift */; }; 13 | F30F64911D46A27500EDD75F /* WatchKit+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359013F1D2A9C9A00868171 /* WatchKit+Johnny.swift */; }; 14 | F30F64921D46A39A00EDD75F /* UIButton+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB051D25634F005B30AB /* UIButton+Johnny.swift */; }; 15 | F319959D1D48D60200429F83 /* JohnnyDiskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F319959C1D48D60200429F83 /* JohnnyDiskTests.swift */; }; 16 | F3382FB91D2594D3009C2110 /* UIColor+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3382FB81D2594D3009C2110 /* UIColor+Johnny.swift */; }; 17 | F3382FBA1D2594D8009C2110 /* UIColor+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3382FB81D2594D3009C2110 /* UIColor+Johnny.swift */; }; 18 | F3382FBB1D2594D9009C2110 /* UIColor+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3382FB81D2594D3009C2110 /* UIColor+Johnny.swift */; }; 19 | F35901381D2A7DEC00868171 /* Foundation+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35901371D2A7DEC00868171 /* Foundation+Johnny.swift */; }; 20 | F35901391D2A7DF100868171 /* Foundation+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35901371D2A7DEC00868171 /* Foundation+Johnny.swift */; }; 21 | F359013A1D2A7DF200868171 /* Foundation+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35901371D2A7DEC00868171 /* Foundation+Johnny.swift */; }; 22 | F359013B1D2A7DF200868171 /* Foundation+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35901371D2A7DEC00868171 /* Foundation+Johnny.swift */; }; 23 | F359013E1D2A822400868171 /* AppKit+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359013C1D2A820900868171 /* AppKit+Johnny.swift */; }; 24 | F37A20541D48A042007603F5 /* JohnnyMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37A20531D48A042007603F5 /* JohnnyMemoryTests.swift */; }; 25 | F37A20561D48A042007603F5 /* Johnny.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3E4CAF31D256069005B30AB /* Johnny.framework */; }; 26 | F37A205F1D48AC39007603F5 /* johnny-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = F35901881D2AA55400868171 /* johnny-logo.png */; }; 27 | F37A20601D48AC3A007603F5 /* johnny-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = F35901881D2AA55400868171 /* johnny-logo.png */; }; 28 | F3E4CAF71D256069005B30AB /* Johnny.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CAF61D256069005B30AB /* Johnny.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | F3E4CB0B1D25634F005B30AB /* Disk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB001D25634F005B30AB /* Disk.swift */; }; 30 | F3E4CB101D25634F005B30AB /* UIImage+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB061D25634F005B30AB /* UIImage+Johnny.swift */; }; 31 | F3E4CB111D25634F005B30AB /* UIImageView+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB071D25634F005B30AB /* UIImageView+Johnny.swift */; }; 32 | F3E4CB121D25634F005B30AB /* Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB081D25634F005B30AB /* Johnny.swift */; }; 33 | F3E4CB131D25634F005B30AB /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB091D25634F005B30AB /* Log.swift */; }; 34 | F3E4CB141D25634F005B30AB /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB0A1D25634F005B30AB /* Memory.swift */; }; 35 | F3E4CB341D2572FC005B30AB /* Johnny macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CB331D2572FC005B30AB /* Johnny macOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 36 | F3E4CB411D2573CD005B30AB /* Johnny tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CB401D2573CD005B30AB /* Johnny tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 37 | F3E4CB4E1D2573FF005B30AB /* Johnny watchOS.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CB4D1D2573FF005B30AB /* Johnny watchOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 38 | F3E4CB531D25754C005B30AB /* Johnny.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CAF61D256069005B30AB /* Johnny.h */; }; 39 | F3E4CB541D25754C005B30AB /* Johnny.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CAF61D256069005B30AB /* Johnny.h */; }; 40 | F3E4CB551D25754D005B30AB /* Johnny.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E4CAF61D256069005B30AB /* Johnny.h */; }; 41 | F3E4CB561D257552005B30AB /* Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB081D25634F005B30AB /* Johnny.swift */; }; 42 | F3E4CB571D257552005B30AB /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB0A1D25634F005B30AB /* Memory.swift */; }; 43 | F3E4CB581D257552005B30AB /* Disk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB001D25634F005B30AB /* Disk.swift */; }; 44 | F3E4CB591D257553005B30AB /* Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB081D25634F005B30AB /* Johnny.swift */; }; 45 | F3E4CB5A1D257553005B30AB /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB0A1D25634F005B30AB /* Memory.swift */; }; 46 | F3E4CB5B1D257553005B30AB /* Disk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB001D25634F005B30AB /* Disk.swift */; }; 47 | F3E4CB5C1D257554005B30AB /* Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB081D25634F005B30AB /* Johnny.swift */; }; 48 | F3E4CB5D1D257554005B30AB /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB0A1D25634F005B30AB /* Memory.swift */; }; 49 | F3E4CB5E1D257554005B30AB /* Disk.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB001D25634F005B30AB /* Disk.swift */; }; 50 | F3E4CB6A1D25755E005B30AB /* UIImageView+Johnny.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB071D25634F005B30AB /* UIImageView+Johnny.swift */; }; 51 | F3E4CB711D25756A005B30AB /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB091D25634F005B30AB /* Log.swift */; }; 52 | F3E4CB721D25756B005B30AB /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB091D25634F005B30AB /* Log.swift */; }; 53 | F3E4CB731D25756B005B30AB /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E4CB091D25634F005B30AB /* Log.swift */; }; 54 | /* End PBXBuildFile section */ 55 | 56 | /* Begin PBXContainerItemProxy section */ 57 | F37A20571D48A042007603F5 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = F3E4CAEA1D256069005B30AB /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = F3E4CAF21D256069005B30AB; 62 | remoteInfo = "Johnny iOS"; 63 | }; 64 | /* End PBXContainerItemProxy section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | F3036D321D257FC50056BCE4 /* macOS.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = macOS.modulemap; sourceTree = ""; }; 68 | F319959C1D48D60200429F83 /* JohnnyDiskTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JohnnyDiskTests.swift; sourceTree = ""; }; 69 | F31E2FE21D49F10B009EE3F1 /* johhny-logo.sketch */ = {isa = PBXFileReference; lastKnownFileType = file; path = "johhny-logo.sketch"; sourceTree = SOURCE_ROOT; }; 70 | F31E2FE41D4A1016009EE3F1 /* tags.sketch */ = {isa = PBXFileReference; lastKnownFileType = file; path = tags.sketch; sourceTree = SOURCE_ROOT; }; 71 | F31E2FE51D4A1061009EE3F1 /* language.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = language.svg; sourceTree = SOURCE_ROOT; }; 72 | F31E2FE61D4A1061009EE3F1 /* license.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = license.svg; sourceTree = SOURCE_ROOT; }; 73 | F31E2FE71D4A1061009EE3F1 /* platform.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = platform.svg; sourceTree = SOURCE_ROOT; }; 74 | F31E2FE81D4A1061009EE3F1 /* pod.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = pod.svg; sourceTree = SOURCE_ROOT; }; 75 | F33542F91D258A3D00E3026F /* tvOS.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = tvOS.modulemap; sourceTree = ""; }; 76 | F3382FB71D259384009C2110 /* watchOS.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = watchOS.modulemap; sourceTree = ""; }; 77 | F3382FB81D2594D3009C2110 /* UIColor+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Johnny.swift"; sourceTree = ""; }; 78 | F35901371D2A7DEC00868171 /* Foundation+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Foundation+Johnny.swift"; sourceTree = ""; }; 79 | F359013C1D2A820900868171 /* AppKit+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppKit+Johnny.swift"; sourceTree = ""; }; 80 | F359013F1D2A9C9A00868171 /* WatchKit+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WatchKit+Johnny.swift"; sourceTree = ""; }; 81 | F35901881D2AA55400868171 /* johnny-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "johnny-logo.png"; sourceTree = ""; }; 82 | F359018B1D2AC51500868171 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; 83 | F359018D1D2AC65800868171 /* Johnny.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Johnny.podspec; sourceTree = SOURCE_ROOT; }; 84 | F37A20511D48A042007603F5 /* JohnnyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JohnnyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | F37A20531D48A042007603F5 /* JohnnyMemoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JohnnyMemoryTests.swift; sourceTree = ""; }; 86 | F37A20551D48A042007603F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 87 | F3E4CAF31D256069005B30AB /* Johnny.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Johnny.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | F3E4CAF61D256069005B30AB /* Johnny.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Johnny.h; sourceTree = ""; }; 89 | F3E4CAF81D256069005B30AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 90 | F3E4CB001D25634F005B30AB /* Disk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disk.swift; sourceTree = ""; }; 91 | F3E4CB051D25634F005B30AB /* UIButton+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Johnny.swift"; sourceTree = ""; }; 92 | F3E4CB061D25634F005B30AB /* UIImage+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Johnny.swift"; sourceTree = ""; }; 93 | F3E4CB071D25634F005B30AB /* UIImageView+Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Johnny.swift"; sourceTree = ""; }; 94 | F3E4CB081D25634F005B30AB /* Johnny.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Johnny.swift; sourceTree = ""; }; 95 | F3E4CB091D25634F005B30AB /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; 96 | F3E4CB0A1D25634F005B30AB /* Memory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Memory.swift; sourceTree = ""; }; 97 | F3E4CB311D2572FC005B30AB /* Johnny.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Johnny.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | F3E4CB331D2572FC005B30AB /* Johnny macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Johnny macOS.h"; sourceTree = ""; }; 99 | F3E4CB351D2572FC005B30AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | F3E4CB3E1D2573CD005B30AB /* Johnny.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Johnny.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | F3E4CB401D2573CD005B30AB /* Johnny tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Johnny tvOS.h"; sourceTree = ""; }; 102 | F3E4CB421D2573CD005B30AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 103 | F3E4CB4B1D2573FF005B30AB /* Johnny.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Johnny.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 104 | F3E4CB4D1D2573FF005B30AB /* Johnny watchOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Johnny watchOS.h"; sourceTree = ""; }; 105 | F3E4CB4F1D2573FF005B30AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 106 | /* End PBXFileReference section */ 107 | 108 | /* Begin PBXFrameworksBuildPhase section */ 109 | F37A204E1D48A042007603F5 /* Frameworks */ = { 110 | isa = PBXFrameworksBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | F37A20561D48A042007603F5 /* Johnny.framework in Frameworks */, 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | F3E4CAEF1D256069005B30AB /* Frameworks */ = { 118 | isa = PBXFrameworksBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | F3E4CB2D1D2572FC005B30AB /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | F3E4CB3A1D2573CD005B30AB /* Frameworks */ = { 132 | isa = PBXFrameworksBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | F3E4CB471D2573FF005B30AB /* Frameworks */ = { 139 | isa = PBXFrameworksBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXFrameworksBuildPhase section */ 146 | 147 | /* Begin PBXGroup section */ 148 | F359018C1D2AC51E00868171 /* Configuration */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | F3E4CAF81D256069005B30AB /* Info.plist */, 152 | F359018B1D2AC51500868171 /* README.md */, 153 | F359018D1D2AC65800868171 /* Johnny.podspec */, 154 | F35901881D2AA55400868171 /* johnny-logo.png */, 155 | F31E2FE21D49F10B009EE3F1 /* johhny-logo.sketch */, 156 | F31E2FE41D4A1016009EE3F1 /* tags.sketch */, 157 | F31E2FE51D4A1061009EE3F1 /* language.svg */, 158 | F31E2FE61D4A1061009EE3F1 /* license.svg */, 159 | F31E2FE71D4A1061009EE3F1 /* platform.svg */, 160 | F31E2FE81D4A1061009EE3F1 /* pod.svg */, 161 | ); 162 | name = Configuration; 163 | sourceTree = ""; 164 | }; 165 | F37A20521D48A042007603F5 /* JohnnyTests */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | F37A20531D48A042007603F5 /* JohnnyMemoryTests.swift */, 169 | F319959C1D48D60200429F83 /* JohnnyDiskTests.swift */, 170 | F37A20551D48A042007603F5 /* Info.plist */, 171 | ); 172 | path = JohnnyTests; 173 | sourceTree = ""; 174 | }; 175 | F3D0533D1D47E26A00D9AA84 /* common */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | F35901371D2A7DEC00868171 /* Foundation+Johnny.swift */, 179 | ); 180 | path = common; 181 | sourceTree = ""; 182 | }; 183 | F3D0533F1D47E26A00D9AA84 /* macOS */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | F359013C1D2A820900868171 /* AppKit+Johnny.swift */, 187 | ); 188 | path = macOS; 189 | sourceTree = ""; 190 | }; 191 | F3D053411D47E26A00D9AA84 /* watchOS */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | F359013F1D2A9C9A00868171 /* WatchKit+Johnny.swift */, 195 | ); 196 | path = watchOS; 197 | sourceTree = ""; 198 | }; 199 | F3E4CAE91D256069005B30AB = { 200 | isa = PBXGroup; 201 | children = ( 202 | F3E4CAF51D256069005B30AB /* Johnny */, 203 | F3E4CB321D2572FC005B30AB /* Johnny macOS */, 204 | F3E4CB3F1D2573CD005B30AB /* Johnny tvOS */, 205 | F3E4CB4C1D2573FF005B30AB /* Johnny watchOS */, 206 | F37A20521D48A042007603F5 /* JohnnyTests */, 207 | F3E4CAF41D256069005B30AB /* Products */, 208 | ); 209 | sourceTree = ""; 210 | }; 211 | F3E4CAF41D256069005B30AB /* Products */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | F3E4CAF31D256069005B30AB /* Johnny.framework */, 215 | F3E4CB311D2572FC005B30AB /* Johnny.framework */, 216 | F3E4CB3E1D2573CD005B30AB /* Johnny.framework */, 217 | F3E4CB4B1D2573FF005B30AB /* Johnny.framework */, 218 | F37A20511D48A042007603F5 /* JohnnyTests.xctest */, 219 | ); 220 | name = Products; 221 | sourceTree = ""; 222 | }; 223 | F3E4CAF51D256069005B30AB /* Johnny */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | F3E4CAF61D256069005B30AB /* Johnny.h */, 227 | F3E4CB081D25634F005B30AB /* Johnny.swift */, 228 | F3E4CB0A1D25634F005B30AB /* Memory.swift */, 229 | F3E4CB001D25634F005B30AB /* Disk.swift */, 230 | F3E4CB011D25634F005B30AB /* Extensions */, 231 | F3E4CB091D25634F005B30AB /* Log.swift */, 232 | F359018C1D2AC51E00868171 /* Configuration */, 233 | ); 234 | path = Johnny; 235 | sourceTree = ""; 236 | }; 237 | F3E4CB011D25634F005B30AB /* Extensions */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | F3D0533D1D47E26A00D9AA84 /* common */, 241 | F3D0533F1D47E26A00D9AA84 /* macOS */, 242 | F3D053411D47E26A00D9AA84 /* watchOS */, 243 | F3E4CB051D25634F005B30AB /* UIButton+Johnny.swift */, 244 | F3382FB81D2594D3009C2110 /* UIColor+Johnny.swift */, 245 | F3E4CB061D25634F005B30AB /* UIImage+Johnny.swift */, 246 | F3E4CB071D25634F005B30AB /* UIImageView+Johnny.swift */, 247 | ); 248 | path = Extensions; 249 | sourceTree = ""; 250 | }; 251 | F3E4CB321D2572FC005B30AB /* Johnny macOS */ = { 252 | isa = PBXGroup; 253 | children = ( 254 | F3E4CB331D2572FC005B30AB /* Johnny macOS.h */, 255 | F3E4CB351D2572FC005B30AB /* Info.plist */, 256 | F3036D321D257FC50056BCE4 /* macOS.modulemap */, 257 | ); 258 | path = "Johnny macOS"; 259 | sourceTree = ""; 260 | }; 261 | F3E4CB3F1D2573CD005B30AB /* Johnny tvOS */ = { 262 | isa = PBXGroup; 263 | children = ( 264 | F3E4CB401D2573CD005B30AB /* Johnny tvOS.h */, 265 | F3E4CB421D2573CD005B30AB /* Info.plist */, 266 | F33542F91D258A3D00E3026F /* tvOS.modulemap */, 267 | ); 268 | path = "Johnny tvOS"; 269 | sourceTree = ""; 270 | }; 271 | F3E4CB4C1D2573FF005B30AB /* Johnny watchOS */ = { 272 | isa = PBXGroup; 273 | children = ( 274 | F3E4CB4D1D2573FF005B30AB /* Johnny watchOS.h */, 275 | F3E4CB4F1D2573FF005B30AB /* Info.plist */, 276 | F3382FB71D259384009C2110 /* watchOS.modulemap */, 277 | ); 278 | path = "Johnny watchOS"; 279 | sourceTree = ""; 280 | }; 281 | /* End PBXGroup section */ 282 | 283 | /* Begin PBXHeadersBuildPhase section */ 284 | F3E4CAF01D256069005B30AB /* Headers */ = { 285 | isa = PBXHeadersBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | F3E4CAF71D256069005B30AB /* Johnny.h in Headers */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | F3E4CB2E1D2572FC005B30AB /* Headers */ = { 293 | isa = PBXHeadersBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | F3E4CB341D2572FC005B30AB /* Johnny macOS.h in Headers */, 297 | F3E4CB531D25754C005B30AB /* Johnny.h in Headers */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | F3E4CB3B1D2573CD005B30AB /* Headers */ = { 302 | isa = PBXHeadersBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | F3E4CB541D25754C005B30AB /* Johnny.h in Headers */, 306 | F3E4CB411D2573CD005B30AB /* Johnny tvOS.h in Headers */, 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | F3E4CB481D2573FF005B30AB /* Headers */ = { 311 | isa = PBXHeadersBuildPhase; 312 | buildActionMask = 2147483647; 313 | files = ( 314 | F3E4CB551D25754D005B30AB /* Johnny.h in Headers */, 315 | F3E4CB4E1D2573FF005B30AB /* Johnny watchOS.h in Headers */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | /* End PBXHeadersBuildPhase section */ 320 | 321 | /* Begin PBXNativeTarget section */ 322 | F37A20501D48A042007603F5 /* JohnnyTests */ = { 323 | isa = PBXNativeTarget; 324 | buildConfigurationList = F37A205B1D48A042007603F5 /* Build configuration list for PBXNativeTarget "JohnnyTests" */; 325 | buildPhases = ( 326 | F37A204D1D48A042007603F5 /* Sources */, 327 | F37A204E1D48A042007603F5 /* Frameworks */, 328 | F37A204F1D48A042007603F5 /* Resources */, 329 | ); 330 | buildRules = ( 331 | ); 332 | dependencies = ( 333 | F37A20581D48A042007603F5 /* PBXTargetDependency */, 334 | ); 335 | name = JohnnyTests; 336 | productName = JohnnyTests; 337 | productReference = F37A20511D48A042007603F5 /* JohnnyTests.xctest */; 338 | productType = "com.apple.product-type.bundle.unit-test"; 339 | }; 340 | F3E4CAF21D256069005B30AB /* Johnny iOS */ = { 341 | isa = PBXNativeTarget; 342 | buildConfigurationList = F3E4CAFB1D256069005B30AB /* Build configuration list for PBXNativeTarget "Johnny iOS" */; 343 | buildPhases = ( 344 | F3E4CAEE1D256069005B30AB /* Sources */, 345 | F3E4CAEF1D256069005B30AB /* Frameworks */, 346 | F3E4CAF01D256069005B30AB /* Headers */, 347 | F3E4CAF11D256069005B30AB /* Resources */, 348 | ); 349 | buildRules = ( 350 | ); 351 | dependencies = ( 352 | ); 353 | name = "Johnny iOS"; 354 | productName = Johnny; 355 | productReference = F3E4CAF31D256069005B30AB /* Johnny.framework */; 356 | productType = "com.apple.product-type.framework"; 357 | }; 358 | F3E4CB301D2572FC005B30AB /* Johnny macOS */ = { 359 | isa = PBXNativeTarget; 360 | buildConfigurationList = F3E4CB381D2572FC005B30AB /* Build configuration list for PBXNativeTarget "Johnny macOS" */; 361 | buildPhases = ( 362 | F3E4CB2C1D2572FC005B30AB /* Sources */, 363 | F3E4CB2D1D2572FC005B30AB /* Frameworks */, 364 | F3E4CB2E1D2572FC005B30AB /* Headers */, 365 | F3E4CB2F1D2572FC005B30AB /* Resources */, 366 | ); 367 | buildRules = ( 368 | ); 369 | dependencies = ( 370 | ); 371 | name = "Johnny macOS"; 372 | productName = "Johnny macOS"; 373 | productReference = F3E4CB311D2572FC005B30AB /* Johnny.framework */; 374 | productType = "com.apple.product-type.framework"; 375 | }; 376 | F3E4CB3D1D2573CD005B30AB /* Johnny tvOS */ = { 377 | isa = PBXNativeTarget; 378 | buildConfigurationList = F3E4CB431D2573CD005B30AB /* Build configuration list for PBXNativeTarget "Johnny tvOS" */; 379 | buildPhases = ( 380 | F3E4CB391D2573CD005B30AB /* Sources */, 381 | F3E4CB3A1D2573CD005B30AB /* Frameworks */, 382 | F3E4CB3B1D2573CD005B30AB /* Headers */, 383 | F3E4CB3C1D2573CD005B30AB /* Resources */, 384 | ); 385 | buildRules = ( 386 | ); 387 | dependencies = ( 388 | ); 389 | name = "Johnny tvOS"; 390 | productName = "Johnny tvOS"; 391 | productReference = F3E4CB3E1D2573CD005B30AB /* Johnny.framework */; 392 | productType = "com.apple.product-type.framework"; 393 | }; 394 | F3E4CB4A1D2573FF005B30AB /* Johnny watchOS */ = { 395 | isa = PBXNativeTarget; 396 | buildConfigurationList = F3E4CB501D2573FF005B30AB /* Build configuration list for PBXNativeTarget "Johnny watchOS" */; 397 | buildPhases = ( 398 | F3E4CB461D2573FF005B30AB /* Sources */, 399 | F3E4CB471D2573FF005B30AB /* Frameworks */, 400 | F3E4CB481D2573FF005B30AB /* Headers */, 401 | F3E4CB491D2573FF005B30AB /* Resources */, 402 | ); 403 | buildRules = ( 404 | ); 405 | dependencies = ( 406 | ); 407 | name = "Johnny watchOS"; 408 | productName = "Johnny watchOS"; 409 | productReference = F3E4CB4B1D2573FF005B30AB /* Johnny.framework */; 410 | productType = "com.apple.product-type.framework"; 411 | }; 412 | /* End PBXNativeTarget section */ 413 | 414 | /* Begin PBXProject section */ 415 | F3E4CAEA1D256069005B30AB /* Project object */ = { 416 | isa = PBXProject; 417 | attributes = { 418 | LastSwiftUpdateCheck = 0730; 419 | LastUpgradeCheck = 1010; 420 | ORGANIZATIONNAME = "Zoltán Matók"; 421 | TargetAttributes = { 422 | F37A20501D48A042007603F5 = { 423 | CreatedOnToolsVersion = 7.3.1; 424 | LastSwiftMigration = 0900; 425 | }; 426 | F3E4CAF21D256069005B30AB = { 427 | CreatedOnToolsVersion = 7.3.1; 428 | DevelopmentTeam = 83655RR9FZ; 429 | LastSwiftMigration = 0900; 430 | }; 431 | F3E4CB301D2572FC005B30AB = { 432 | CreatedOnToolsVersion = 7.3.1; 433 | LastSwiftMigration = 0820; 434 | }; 435 | F3E4CB3D1D2573CD005B30AB = { 436 | CreatedOnToolsVersion = 7.3.1; 437 | DevelopmentTeam = 83655RR9FZ; 438 | LastSwiftMigration = 0820; 439 | }; 440 | F3E4CB4A1D2573FF005B30AB = { 441 | CreatedOnToolsVersion = 7.3.1; 442 | DevelopmentTeam = 83655RR9FZ; 443 | LastSwiftMigration = 0820; 444 | }; 445 | }; 446 | }; 447 | buildConfigurationList = F3E4CAED1D256069005B30AB /* Build configuration list for PBXProject "Johnny" */; 448 | compatibilityVersion = "Xcode 3.2"; 449 | developmentRegion = English; 450 | hasScannedForEncodings = 0; 451 | knownRegions = ( 452 | English, 453 | en, 454 | ); 455 | mainGroup = F3E4CAE91D256069005B30AB; 456 | productRefGroup = F3E4CAF41D256069005B30AB /* Products */; 457 | projectDirPath = ""; 458 | projectRoot = ""; 459 | targets = ( 460 | F3E4CAF21D256069005B30AB /* Johnny iOS */, 461 | F3E4CB301D2572FC005B30AB /* Johnny macOS */, 462 | F3E4CB3D1D2573CD005B30AB /* Johnny tvOS */, 463 | F3E4CB4A1D2573FF005B30AB /* Johnny watchOS */, 464 | F37A20501D48A042007603F5 /* JohnnyTests */, 465 | ); 466 | }; 467 | /* End PBXProject section */ 468 | 469 | /* Begin PBXResourcesBuildPhase section */ 470 | F37A204F1D48A042007603F5 /* Resources */ = { 471 | isa = PBXResourcesBuildPhase; 472 | buildActionMask = 2147483647; 473 | files = ( 474 | F37A20601D48AC3A007603F5 /* johnny-logo.png in Resources */, 475 | ); 476 | runOnlyForDeploymentPostprocessing = 0; 477 | }; 478 | F3E4CAF11D256069005B30AB /* Resources */ = { 479 | isa = PBXResourcesBuildPhase; 480 | buildActionMask = 2147483647; 481 | files = ( 482 | F37A205F1D48AC39007603F5 /* johnny-logo.png in Resources */, 483 | ); 484 | runOnlyForDeploymentPostprocessing = 0; 485 | }; 486 | F3E4CB2F1D2572FC005B30AB /* Resources */ = { 487 | isa = PBXResourcesBuildPhase; 488 | buildActionMask = 2147483647; 489 | files = ( 490 | ); 491 | runOnlyForDeploymentPostprocessing = 0; 492 | }; 493 | F3E4CB3C1D2573CD005B30AB /* Resources */ = { 494 | isa = PBXResourcesBuildPhase; 495 | buildActionMask = 2147483647; 496 | files = ( 497 | ); 498 | runOnlyForDeploymentPostprocessing = 0; 499 | }; 500 | F3E4CB491D2573FF005B30AB /* Resources */ = { 501 | isa = PBXResourcesBuildPhase; 502 | buildActionMask = 2147483647; 503 | files = ( 504 | ); 505 | runOnlyForDeploymentPostprocessing = 0; 506 | }; 507 | /* End PBXResourcesBuildPhase section */ 508 | 509 | /* Begin PBXSourcesBuildPhase section */ 510 | F37A204D1D48A042007603F5 /* Sources */ = { 511 | isa = PBXSourcesBuildPhase; 512 | buildActionMask = 2147483647; 513 | files = ( 514 | F319959D1D48D60200429F83 /* JohnnyDiskTests.swift in Sources */, 515 | F37A20541D48A042007603F5 /* JohnnyMemoryTests.swift in Sources */, 516 | ); 517 | runOnlyForDeploymentPostprocessing = 0; 518 | }; 519 | F3E4CAEE1D256069005B30AB /* Sources */ = { 520 | isa = PBXSourcesBuildPhase; 521 | buildActionMask = 2147483647; 522 | files = ( 523 | F3E4CB111D25634F005B30AB /* UIImageView+Johnny.swift in Sources */, 524 | F35901381D2A7DEC00868171 /* Foundation+Johnny.swift in Sources */, 525 | F3E4CB131D25634F005B30AB /* Log.swift in Sources */, 526 | F3036D361D2584A80056BCE4 /* UIButton+Johnny.swift in Sources */, 527 | F3382FB91D2594D3009C2110 /* UIColor+Johnny.swift in Sources */, 528 | F3E4CB121D25634F005B30AB /* Johnny.swift in Sources */, 529 | F3E4CB0B1D25634F005B30AB /* Disk.swift in Sources */, 530 | F3E4CB141D25634F005B30AB /* Memory.swift in Sources */, 531 | F3E4CB101D25634F005B30AB /* UIImage+Johnny.swift in Sources */, 532 | ); 533 | runOnlyForDeploymentPostprocessing = 0; 534 | }; 535 | F3E4CB2C1D2572FC005B30AB /* Sources */ = { 536 | isa = PBXSourcesBuildPhase; 537 | buildActionMask = 2147483647; 538 | files = ( 539 | F3E4CB711D25756A005B30AB /* Log.swift in Sources */, 540 | F35901391D2A7DF100868171 /* Foundation+Johnny.swift in Sources */, 541 | F359013E1D2A822400868171 /* AppKit+Johnny.swift in Sources */, 542 | F3E4CB581D257552005B30AB /* Disk.swift in Sources */, 543 | F3E4CB571D257552005B30AB /* Memory.swift in Sources */, 544 | F3E4CB561D257552005B30AB /* Johnny.swift in Sources */, 545 | ); 546 | runOnlyForDeploymentPostprocessing = 0; 547 | }; 548 | F3E4CB391D2573CD005B30AB /* Sources */ = { 549 | isa = PBXSourcesBuildPhase; 550 | buildActionMask = 2147483647; 551 | files = ( 552 | F359013A1D2A7DF200868171 /* Foundation+Johnny.swift in Sources */, 553 | F30F64921D46A39A00EDD75F /* UIButton+Johnny.swift in Sources */, 554 | F3E4CB721D25756B005B30AB /* Log.swift in Sources */, 555 | F3382FBA1D2594D8009C2110 /* UIColor+Johnny.swift in Sources */, 556 | F3E4CB5B1D257553005B30AB /* Disk.swift in Sources */, 557 | F3036D381D2584C30056BCE4 /* UIImage+Johnny.swift in Sources */, 558 | F3E4CB5A1D257553005B30AB /* Memory.swift in Sources */, 559 | F3E4CB591D257553005B30AB /* Johnny.swift in Sources */, 560 | F3E4CB6A1D25755E005B30AB /* UIImageView+Johnny.swift in Sources */, 561 | ); 562 | runOnlyForDeploymentPostprocessing = 0; 563 | }; 564 | F3E4CB461D2573FF005B30AB /* Sources */ = { 565 | isa = PBXSourcesBuildPhase; 566 | buildActionMask = 2147483647; 567 | files = ( 568 | F359013B1D2A7DF200868171 /* Foundation+Johnny.swift in Sources */, 569 | F3E4CB731D25756B005B30AB /* Log.swift in Sources */, 570 | F30F64911D46A27500EDD75F /* WatchKit+Johnny.swift in Sources */, 571 | F3E4CB5E1D257554005B30AB /* Disk.swift in Sources */, 572 | F3382FBB1D2594D9009C2110 /* UIColor+Johnny.swift in Sources */, 573 | F3036D371D2584B00056BCE4 /* UIImage+Johnny.swift in Sources */, 574 | F3E4CB5D1D257554005B30AB /* Memory.swift in Sources */, 575 | F3E4CB5C1D257554005B30AB /* Johnny.swift in Sources */, 576 | ); 577 | runOnlyForDeploymentPostprocessing = 0; 578 | }; 579 | /* End PBXSourcesBuildPhase section */ 580 | 581 | /* Begin PBXTargetDependency section */ 582 | F37A20581D48A042007603F5 /* PBXTargetDependency */ = { 583 | isa = PBXTargetDependency; 584 | target = F3E4CAF21D256069005B30AB /* Johnny iOS */; 585 | targetProxy = F37A20571D48A042007603F5 /* PBXContainerItemProxy */; 586 | }; 587 | /* End PBXTargetDependency section */ 588 | 589 | /* Begin XCBuildConfiguration section */ 590 | F37A20591D48A042007603F5 /* Debug */ = { 591 | isa = XCBuildConfiguration; 592 | buildSettings = { 593 | INFOPLIST_FILE = JohnnyTests/Info.plist; 594 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 595 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 596 | PRODUCT_BUNDLE_IDENTIFIER = ZoltanMatok.JohnnyTests; 597 | PRODUCT_NAME = "$(TARGET_NAME)"; 598 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 599 | SWIFT_VERSION = 4.0; 600 | }; 601 | name = Debug; 602 | }; 603 | F37A205A1D48A042007603F5 /* Release */ = { 604 | isa = XCBuildConfiguration; 605 | buildSettings = { 606 | INFOPLIST_FILE = JohnnyTests/Info.plist; 607 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 608 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 609 | PRODUCT_BUNDLE_IDENTIFIER = ZoltanMatok.JohnnyTests; 610 | PRODUCT_NAME = "$(TARGET_NAME)"; 611 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 612 | SWIFT_VERSION = 4.0; 613 | }; 614 | name = Release; 615 | }; 616 | F3E4CAF91D256069005B30AB /* Debug */ = { 617 | isa = XCBuildConfiguration; 618 | buildSettings = { 619 | ALWAYS_SEARCH_USER_PATHS = NO; 620 | CLANG_ANALYZER_NONNULL = YES; 621 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 622 | CLANG_CXX_LIBRARY = "libc++"; 623 | CLANG_ENABLE_MODULES = YES; 624 | CLANG_ENABLE_OBJC_ARC = YES; 625 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 626 | CLANG_WARN_BOOL_CONVERSION = YES; 627 | CLANG_WARN_COMMA = YES; 628 | CLANG_WARN_CONSTANT_CONVERSION = YES; 629 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 630 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 631 | CLANG_WARN_EMPTY_BODY = YES; 632 | CLANG_WARN_ENUM_CONVERSION = YES; 633 | CLANG_WARN_INFINITE_RECURSION = YES; 634 | CLANG_WARN_INT_CONVERSION = YES; 635 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 636 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 637 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 638 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 639 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 640 | CLANG_WARN_STRICT_PROTOTYPES = YES; 641 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 642 | CLANG_WARN_UNREACHABLE_CODE = YES; 643 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 644 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 645 | COPY_PHASE_STRIP = NO; 646 | CURRENT_PROJECT_VERSION = 1; 647 | DEBUG_INFORMATION_FORMAT = dwarf; 648 | ENABLE_STRICT_OBJC_MSGSEND = YES; 649 | ENABLE_TESTABILITY = YES; 650 | GCC_C_LANGUAGE_STANDARD = gnu99; 651 | GCC_DYNAMIC_NO_PIC = NO; 652 | GCC_NO_COMMON_BLOCKS = YES; 653 | GCC_OPTIMIZATION_LEVEL = 0; 654 | GCC_PREPROCESSOR_DEFINITIONS = ( 655 | "DEBUG=1", 656 | "$(inherited)", 657 | ); 658 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 659 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 660 | GCC_WARN_UNDECLARED_SELECTOR = YES; 661 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 662 | GCC_WARN_UNUSED_FUNCTION = YES; 663 | GCC_WARN_UNUSED_VARIABLE = YES; 664 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 665 | MTL_ENABLE_DEBUG_INFO = YES; 666 | ONLY_ACTIVE_ARCH = YES; 667 | SDKROOT = iphoneos; 668 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 669 | TARGETED_DEVICE_FAMILY = "1,2"; 670 | VERSIONING_SYSTEM = "apple-generic"; 671 | VERSION_INFO_PREFIX = ""; 672 | }; 673 | name = Debug; 674 | }; 675 | F3E4CAFA1D256069005B30AB /* Release */ = { 676 | isa = XCBuildConfiguration; 677 | buildSettings = { 678 | ALWAYS_SEARCH_USER_PATHS = NO; 679 | CLANG_ANALYZER_NONNULL = YES; 680 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 681 | CLANG_CXX_LIBRARY = "libc++"; 682 | CLANG_ENABLE_MODULES = YES; 683 | CLANG_ENABLE_OBJC_ARC = YES; 684 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 685 | CLANG_WARN_BOOL_CONVERSION = YES; 686 | CLANG_WARN_COMMA = YES; 687 | CLANG_WARN_CONSTANT_CONVERSION = YES; 688 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 689 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 690 | CLANG_WARN_EMPTY_BODY = YES; 691 | CLANG_WARN_ENUM_CONVERSION = YES; 692 | CLANG_WARN_INFINITE_RECURSION = YES; 693 | CLANG_WARN_INT_CONVERSION = YES; 694 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 695 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 696 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 697 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 698 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 699 | CLANG_WARN_STRICT_PROTOTYPES = YES; 700 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 701 | CLANG_WARN_UNREACHABLE_CODE = YES; 702 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 703 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 704 | COPY_PHASE_STRIP = NO; 705 | CURRENT_PROJECT_VERSION = 1; 706 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 707 | ENABLE_NS_ASSERTIONS = NO; 708 | ENABLE_STRICT_OBJC_MSGSEND = YES; 709 | GCC_C_LANGUAGE_STANDARD = gnu99; 710 | GCC_NO_COMMON_BLOCKS = YES; 711 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 712 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 713 | GCC_WARN_UNDECLARED_SELECTOR = YES; 714 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 715 | GCC_WARN_UNUSED_FUNCTION = YES; 716 | GCC_WARN_UNUSED_VARIABLE = YES; 717 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 718 | MTL_ENABLE_DEBUG_INFO = NO; 719 | SDKROOT = iphoneos; 720 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 721 | TARGETED_DEVICE_FAMILY = "1,2"; 722 | VALIDATE_PRODUCT = YES; 723 | VERSIONING_SYSTEM = "apple-generic"; 724 | VERSION_INFO_PREFIX = ""; 725 | }; 726 | name = Release; 727 | }; 728 | F3E4CAFC1D256069005B30AB /* Debug */ = { 729 | isa = XCBuildConfiguration; 730 | buildSettings = { 731 | CLANG_ENABLE_MODULES = YES; 732 | CODE_SIGN_IDENTITY = "iPhone Developer"; 733 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 734 | DEFINES_MODULE = YES; 735 | DEVELOPMENT_TEAM = 83655RR9FZ; 736 | DYLIB_COMPATIBILITY_VERSION = 1; 737 | DYLIB_CURRENT_VERSION = 1; 738 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 739 | INFOPLIST_FILE = Johnny/Info.plist; 740 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 741 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 742 | OTHER_SWIFT_FLAGS = "$(inherited) -DUIKIT_COMPATIBLE"; 743 | PRODUCT_BUNDLE_IDENTIFIER = ZoltanMatok.Johnny; 744 | PRODUCT_NAME = Johnny; 745 | SKIP_INSTALL = YES; 746 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 747 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 748 | SWIFT_VERSION = 4.0; 749 | }; 750 | name = Debug; 751 | }; 752 | F3E4CAFD1D256069005B30AB /* Release */ = { 753 | isa = XCBuildConfiguration; 754 | buildSettings = { 755 | CLANG_ENABLE_MODULES = YES; 756 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 757 | DEFINES_MODULE = YES; 758 | DYLIB_COMPATIBILITY_VERSION = 1; 759 | DYLIB_CURRENT_VERSION = 1; 760 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 761 | INFOPLIST_FILE = Johnny/Info.plist; 762 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 763 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 764 | OTHER_SWIFT_FLAGS = "$(inherited) -DUIKIT_COMPATIBLE"; 765 | PRODUCT_BUNDLE_IDENTIFIER = ZoltanMatok.Johnny; 766 | PRODUCT_NAME = Johnny; 767 | SKIP_INSTALL = YES; 768 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 769 | SWIFT_VERSION = 4.0; 770 | }; 771 | name = Release; 772 | }; 773 | F3E4CB361D2572FC005B30AB /* Debug */ = { 774 | isa = XCBuildConfiguration; 775 | buildSettings = { 776 | CODE_SIGN_IDENTITY = ""; 777 | COMBINE_HIDPI_IMAGES = YES; 778 | DEFINES_MODULE = YES; 779 | DEVELOPMENT_TEAM = ""; 780 | DYLIB_COMPATIBILITY_VERSION = 1; 781 | DYLIB_CURRENT_VERSION = 1; 782 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 783 | FRAMEWORK_VERSION = A; 784 | INFOPLIST_FILE = "Johnny macOS/Info.plist"; 785 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 786 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 787 | MACOSX_DEPLOYMENT_TARGET = 10.10; 788 | MODULEMAP_FILE = "$(SRCROOT)/Johnny macOS/macOS.modulemap"; 789 | PRODUCT_BUNDLE_IDENTIFIER = "ZoltanMatok.Johnny-macOS"; 790 | PRODUCT_NAME = Johnny; 791 | SDKROOT = macosx; 792 | SKIP_INSTALL = YES; 793 | SWIFT_VERSION = 4.0; 794 | }; 795 | name = Debug; 796 | }; 797 | F3E4CB371D2572FC005B30AB /* Release */ = { 798 | isa = XCBuildConfiguration; 799 | buildSettings = { 800 | CODE_SIGN_IDENTITY = ""; 801 | COMBINE_HIDPI_IMAGES = YES; 802 | DEFINES_MODULE = YES; 803 | DYLIB_COMPATIBILITY_VERSION = 1; 804 | DYLIB_CURRENT_VERSION = 1; 805 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 806 | FRAMEWORK_VERSION = A; 807 | INFOPLIST_FILE = "Johnny macOS/Info.plist"; 808 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 809 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 810 | MACOSX_DEPLOYMENT_TARGET = 10.10; 811 | MODULEMAP_FILE = "$(SRCROOT)/Johnny macOS/macOS.modulemap"; 812 | PRODUCT_BUNDLE_IDENTIFIER = "ZoltanMatok.Johnny-macOS"; 813 | PRODUCT_NAME = Johnny; 814 | SDKROOT = macosx; 815 | SKIP_INSTALL = YES; 816 | SWIFT_VERSION = 4.0; 817 | }; 818 | name = Release; 819 | }; 820 | F3E4CB441D2573CD005B30AB /* Debug */ = { 821 | isa = XCBuildConfiguration; 822 | buildSettings = { 823 | CODE_SIGN_IDENTITY = ""; 824 | DEFINES_MODULE = YES; 825 | DEVELOPMENT_TEAM = 83655RR9FZ; 826 | DYLIB_COMPATIBILITY_VERSION = 1; 827 | DYLIB_CURRENT_VERSION = 1; 828 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 829 | INFOPLIST_FILE = "Johnny tvOS/Info.plist"; 830 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 831 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 832 | MODULEMAP_FILE = "$(SRCROOT)/Johnny tvOS/tvOS.modulemap"; 833 | OTHER_SWIFT_FLAGS = "-DUIKIT_COMPATIBLE $(inherited)"; 834 | PRODUCT_BUNDLE_IDENTIFIER = "ZoltanMatok.Johnny-tvOS"; 835 | PRODUCT_NAME = Johnny; 836 | SDKROOT = appletvos; 837 | SKIP_INSTALL = YES; 838 | SWIFT_VERSION = 4.0; 839 | TARGETED_DEVICE_FAMILY = 3; 840 | TVOS_DEPLOYMENT_TARGET = 9.0; 841 | }; 842 | name = Debug; 843 | }; 844 | F3E4CB451D2573CD005B30AB /* Release */ = { 845 | isa = XCBuildConfiguration; 846 | buildSettings = { 847 | CODE_SIGN_IDENTITY = ""; 848 | DEFINES_MODULE = YES; 849 | DYLIB_COMPATIBILITY_VERSION = 1; 850 | DYLIB_CURRENT_VERSION = 1; 851 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 852 | INFOPLIST_FILE = "Johnny tvOS/Info.plist"; 853 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 854 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 855 | MODULEMAP_FILE = "$(SRCROOT)/Johnny tvOS/tvOS.modulemap"; 856 | OTHER_SWIFT_FLAGS = "-DUIKIT_COMPATIBLE $(inherited)"; 857 | PRODUCT_BUNDLE_IDENTIFIER = "ZoltanMatok.Johnny-tvOS"; 858 | PRODUCT_NAME = Johnny; 859 | SDKROOT = appletvos; 860 | SKIP_INSTALL = YES; 861 | SWIFT_VERSION = 4.0; 862 | TARGETED_DEVICE_FAMILY = 3; 863 | TVOS_DEPLOYMENT_TARGET = 9.0; 864 | }; 865 | name = Release; 866 | }; 867 | F3E4CB511D2573FF005B30AB /* Debug */ = { 868 | isa = XCBuildConfiguration; 869 | buildSettings = { 870 | APPLICATION_EXTENSION_API_ONLY = YES; 871 | CODE_SIGN_IDENTITY = ""; 872 | DEFINES_MODULE = YES; 873 | DEVELOPMENT_TEAM = 83655RR9FZ; 874 | DYLIB_COMPATIBILITY_VERSION = 1; 875 | DYLIB_CURRENT_VERSION = 1; 876 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 877 | INFOPLIST_FILE = "Johnny watchOS/Info.plist"; 878 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 879 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 880 | MODULEMAP_FILE = "$(SRCROOT)/Johnny watchOS/watchOS.modulemap"; 881 | OTHER_SWIFT_FLAGS = "-DUIKIT_COMPATIBLE $(inherited) -DWATCHKIT"; 882 | PRODUCT_BUNDLE_IDENTIFIER = "ZoltanMatok.Johnny-watchOS"; 883 | PRODUCT_NAME = Johnny; 884 | SDKROOT = watchos; 885 | SKIP_INSTALL = YES; 886 | SWIFT_VERSION = 4.0; 887 | TARGETED_DEVICE_FAMILY = 4; 888 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 889 | }; 890 | name = Debug; 891 | }; 892 | F3E4CB521D2573FF005B30AB /* Release */ = { 893 | isa = XCBuildConfiguration; 894 | buildSettings = { 895 | APPLICATION_EXTENSION_API_ONLY = YES; 896 | CODE_SIGN_IDENTITY = ""; 897 | DEFINES_MODULE = YES; 898 | DYLIB_COMPATIBILITY_VERSION = 1; 899 | DYLIB_CURRENT_VERSION = 1; 900 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 901 | INFOPLIST_FILE = "Johnny watchOS/Info.plist"; 902 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 903 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 904 | MODULEMAP_FILE = "$(SRCROOT)/Johnny watchOS/watchOS.modulemap"; 905 | OTHER_SWIFT_FLAGS = "-DUIKIT_COMPATIBLE $(inherited) -DWATCHKIT"; 906 | PRODUCT_BUNDLE_IDENTIFIER = "ZoltanMatok.Johnny-watchOS"; 907 | PRODUCT_NAME = Johnny; 908 | SDKROOT = watchos; 909 | SKIP_INSTALL = YES; 910 | SWIFT_VERSION = 4.0; 911 | TARGETED_DEVICE_FAMILY = 4; 912 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 913 | }; 914 | name = Release; 915 | }; 916 | /* End XCBuildConfiguration section */ 917 | 918 | /* Begin XCConfigurationList section */ 919 | F37A205B1D48A042007603F5 /* Build configuration list for PBXNativeTarget "JohnnyTests" */ = { 920 | isa = XCConfigurationList; 921 | buildConfigurations = ( 922 | F37A20591D48A042007603F5 /* Debug */, 923 | F37A205A1D48A042007603F5 /* Release */, 924 | ); 925 | defaultConfigurationIsVisible = 0; 926 | defaultConfigurationName = Release; 927 | }; 928 | F3E4CAED1D256069005B30AB /* Build configuration list for PBXProject "Johnny" */ = { 929 | isa = XCConfigurationList; 930 | buildConfigurations = ( 931 | F3E4CAF91D256069005B30AB /* Debug */, 932 | F3E4CAFA1D256069005B30AB /* Release */, 933 | ); 934 | defaultConfigurationIsVisible = 0; 935 | defaultConfigurationName = Release; 936 | }; 937 | F3E4CAFB1D256069005B30AB /* Build configuration list for PBXNativeTarget "Johnny iOS" */ = { 938 | isa = XCConfigurationList; 939 | buildConfigurations = ( 940 | F3E4CAFC1D256069005B30AB /* Debug */, 941 | F3E4CAFD1D256069005B30AB /* Release */, 942 | ); 943 | defaultConfigurationIsVisible = 0; 944 | defaultConfigurationName = Release; 945 | }; 946 | F3E4CB381D2572FC005B30AB /* Build configuration list for PBXNativeTarget "Johnny macOS" */ = { 947 | isa = XCConfigurationList; 948 | buildConfigurations = ( 949 | F3E4CB361D2572FC005B30AB /* Debug */, 950 | F3E4CB371D2572FC005B30AB /* Release */, 951 | ); 952 | defaultConfigurationIsVisible = 0; 953 | defaultConfigurationName = Release; 954 | }; 955 | F3E4CB431D2573CD005B30AB /* Build configuration list for PBXNativeTarget "Johnny tvOS" */ = { 956 | isa = XCConfigurationList; 957 | buildConfigurations = ( 958 | F3E4CB441D2573CD005B30AB /* Debug */, 959 | F3E4CB451D2573CD005B30AB /* Release */, 960 | ); 961 | defaultConfigurationIsVisible = 0; 962 | defaultConfigurationName = Release; 963 | }; 964 | F3E4CB501D2573FF005B30AB /* Build configuration list for PBXNativeTarget "Johnny watchOS" */ = { 965 | isa = XCConfigurationList; 966 | buildConfigurations = ( 967 | F3E4CB511D2573FF005B30AB /* Debug */, 968 | F3E4CB521D2573FF005B30AB /* Release */, 969 | ); 970 | defaultConfigurationIsVisible = 0; 971 | defaultConfigurationName = Release; 972 | }; 973 | /* End XCConfigurationList section */ 974 | }; 975 | rootObject = F3E4CAEA1D256069005B30AB /* Project object */; 976 | } 977 | --------------------------------------------------------------------------------