├── .gitignore ├── Configs ├── LifetimeTracker.plist └── LifetimeTrackerTests.plist ├── Example ├── .swift-version ├── Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── LifetimeTracker-Example.xcscheme ├── Example.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── LifetimeTracker │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Local Podspecs │ │ └── LifetimeTracker.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── LifetimeTracker │ │ ├── Info.plist │ │ ├── LifetimeTracker-Info.plist │ │ ├── LifetimeTracker-dummy.m │ │ ├── LifetimeTracker-prefix.pch │ │ ├── LifetimeTracker-umbrella.h │ │ ├── LifetimeTracker.debug.xcconfig │ │ ├── LifetimeTracker.modulemap │ │ ├── LifetimeTracker.release.xcconfig │ │ ├── LifetimeTracker.xcconfig │ │ ├── ResourceBundle-LifetimeTracker-Info.plist │ │ └── ResourceBundle-LifetimeTracker-LifetimeTracker-Info.plist │ │ ├── Pods-Example │ │ ├── Info.plist │ │ ├── Pods-Example-Info.plist │ │ ├── Pods-Example-acknowledgements.markdown │ │ ├── Pods-Example-acknowledgements.plist │ │ ├── Pods-Example-dummy.m │ │ ├── Pods-Example-frameworks.sh │ │ ├── Pods-Example-resources.sh │ │ ├── Pods-Example-umbrella.h │ │ ├── Pods-Example.debug.xcconfig │ │ ├── Pods-Example.modulemap │ │ └── Pods-Example.release.xcconfig │ │ └── Pods-Example_Tests │ │ ├── Info.plist │ │ ├── Pods-Example_Tests-Info.plist │ │ ├── Pods-Example_Tests-acknowledgements.markdown │ │ ├── Pods-Example_Tests-acknowledgements.plist │ │ ├── Pods-Example_Tests-dummy.m │ │ ├── Pods-Example_Tests-frameworks.sh │ │ ├── Pods-Example_Tests-resources.sh │ │ ├── Pods-Example_Tests-umbrella.h │ │ ├── Pods-Example_Tests.debug.xcconfig │ │ ├── Pods-Example_Tests.modulemap │ │ └── Pods-Example_Tests.release.xcconfig └── Tests │ ├── Info.plist │ └── Tests.swift ├── ExampleObjC ├── ExampleObjC.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── ExampleObjC.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── ExampleObjC │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── Podfile ├── Podfile.lock └── Pods │ ├── Local Podspecs │ └── LifetimeTracker.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ └── project.pbxproj │ └── Target Support Files │ ├── LifetimeTracker │ ├── Info.plist │ ├── LifetimeTracker-Info.plist │ ├── LifetimeTracker-dummy.m │ ├── LifetimeTracker-prefix.pch │ ├── LifetimeTracker-umbrella.h │ ├── LifetimeTracker.modulemap │ ├── LifetimeTracker.xcconfig │ ├── ResourceBundle-LifetimeTracker-Info.plist │ └── ResourceBundle-LifetimeTracker-LifetimeTracker-Info.plist │ └── Pods-ExampleObjC │ ├── Info.plist │ ├── Pods-ExampleObjC-Info.plist │ ├── Pods-ExampleObjC-acknowledgements.markdown │ ├── Pods-ExampleObjC-acknowledgements.plist │ ├── Pods-ExampleObjC-dummy.m │ ├── Pods-ExampleObjC-frameworks.sh │ ├── Pods-ExampleObjC-resources.sh │ ├── Pods-ExampleObjC-umbrella.h │ ├── Pods-ExampleObjC.debug.xcconfig │ ├── Pods-ExampleObjC.modulemap │ └── Pods-ExampleObjC.release.xcconfig ├── Funding.yml ├── LICENSE ├── LifetimeTracker.podspec ├── LifetimeTracker.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── LifetimeTracker-iOS.xcscheme │ ├── LifetimeTracker-macOS.xcscheme │ ├── LifetimeTracker-tvOS.xcscheme │ └── LifetimeTracker-watchOS.xcscheme ├── Package.swift ├── README.md ├── Resources ├── demo-bar.gif ├── demo-circular.gif └── ignore.swift ├── Sources ├── Bundle+Extensions.swift ├── Constants.swift ├── DeallocTracker.swift ├── Extensions.swift ├── LifetimeTracker.swift ├── Localizable.strings ├── Resources │ ├── BarDashboard.storyboard │ ├── CircularDashboard.storyboard │ ├── DashboardTableView.storyboard │ ├── DashboardTableViewCell.xib │ └── DashboardTableViewHeaderView.xib └── iOS │ ├── SettingsManager.swift │ └── UI │ ├── BarDashboard │ └── BarDashboardViewController.swift │ ├── CircularDashboard │ └── CircularDashboardViewController.swift │ ├── Constants+UIKit.swift │ ├── DashboardTableView │ ├── DashboardTableViewCell.swift │ ├── DashboardTableViewController.swift │ └── DashboardTableViewHeaderView.swift │ ├── Extensions+UIKit.swift │ ├── HideOption.swift │ ├── LifetimeTracker+DashboardView.swift │ └── LifetimeTrackerListViewController.swift └── Tests ├── LifetimeTrackerTests ├── LifetimeTrackerTests.swift └── VisibilityTests.swift └── LinuxMain.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | .DS_Store 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | *.dSYM.zip 30 | *.dSYM 31 | 32 | ## Playgrounds 33 | timeline.xctimeline 34 | playground.xcworkspace 35 | 36 | # Swift Package Manager 37 | # 38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 39 | # Packages/ 40 | # Package.pins 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /Configs/LifetimeTracker.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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2017 Krzysztof Zablocki. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Configs/LifetimeTrackerTests.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 | -------------------------------------------------------------------------------- /Example/.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcshareddata/xcschemes/LifetimeTracker-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/LifetimeTracker/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Krzysztof Zablocki on 07/31/2017. 6 | // Copyright (c) 2017 Krzysztof Zablocki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import LifetimeTracker 11 | 12 | 13 | @UIApplicationMain 14 | class AppDelegate: UIResponder, UIApplicationDelegate { 15 | 16 | var window: UIWindow? 17 | var extraVC = [UIViewController]() 18 | 19 | 20 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 21 | #if DEBUG 22 | LifetimeTracker.setup( 23 | onUpdate: LifetimeTrackerDashboardIntegration( 24 | visibility: .alwaysVisible, 25 | style: .bar, 26 | textColorForNoIssues: .systemGreen, 27 | textColorForLeakDetected: .systemRed 28 | ).refreshUI 29 | ) 30 | #else 31 | #endif 32 | 33 | window = UIWindow() 34 | window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() 35 | window?.makeKeyAndVisible() 36 | return true 37 | } 38 | 39 | func applicationWillResignActive(_ application: UIApplication) { 40 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 41 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 42 | } 43 | 44 | func applicationDidEnterBackground(_ application: UIApplication) { 45 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | func applicationWillEnterForeground(_ application: UIApplication) { 50 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 51 | } 52 | 53 | func applicationDidBecomeActive(_ application: UIApplication) { 54 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 55 | } 56 | 57 | func applicationWillTerminate(_ application: UIApplication) { 58 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 59 | } 60 | 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /Example/LifetimeTracker/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/LifetimeTracker/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Example/LifetimeTracker/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/LifetimeTracker/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/LifetimeTracker/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Krzysztof Zablocki on 07/31/2017. 6 | // Copyright (c) 2017 Krzysztof Zablocki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import LifetimeTracker 11 | 12 | // MARK: - DetailItem - 13 | 14 | class DetailItem: LifetimeTrackable { 15 | 16 | class var lifetimeConfiguration: LifetimeConfiguration { 17 | // There can be up to three 3 instances from the class. But only three in total including the subclasses 18 | return LifetimeConfiguration(maxCount: 3, groupName: "Detail Item", groupMaxCount: 3) 19 | } 20 | 21 | init() { 22 | self.trackLifetime() 23 | } 24 | } 25 | 26 | // MARK: - DetailItem Subclasses 27 | 28 | class AudtioDetailItem: DetailItem { } 29 | class ImageDetailItem: DetailItem { } 30 | class VideoDetailItem: DetailItem { 31 | 32 | override class var lifetimeConfiguration: LifetimeConfiguration { 33 | // There should only be one video item as the memory usage is too high 34 | let configuration = super.lifetimeConfiguration 35 | configuration.maxCount = 1 36 | return configuration 37 | } 38 | } 39 | 40 | // MARK: - ViewController - 41 | 42 | var leakStorage = [AnyObject]() 43 | 44 | class ViewController: UIViewController, LifetimeTrackable { 45 | 46 | static var lifetimeConfiguration = LifetimeConfiguration(maxCount: 1, groupName: "VC") 47 | 48 | // MARK: - Initialization 49 | 50 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 51 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 52 | 53 | trackLifetime() 54 | 55 | } 56 | 57 | required init?(coder aDecoder: NSCoder) { 58 | super.init(coder: aDecoder) 59 | 60 | trackLifetime() 61 | } 62 | 63 | // MARK: - IBActions 64 | 65 | @IBAction func createLeak(_ sender: Any) { 66 | leakStorage.append(ViewController()) 67 | 68 | leakStorage.append(AudtioDetailItem()) 69 | leakStorage.append(ImageDetailItem()) 70 | leakStorage.append(VideoDetailItem()) 71 | } 72 | 73 | @IBAction func removeLeaks(_ sender: Any) { 74 | leakStorage.removeAll() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | use_frameworks! 4 | 5 | target 'Example' do 6 | 7 | pod 'LifetimeTracker', :path => '../' 8 | 9 | target 'Example_Tests' do 10 | inherit! :search_paths 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LifetimeTracker (1.8.1) 3 | 4 | DEPENDENCIES: 5 | - LifetimeTracker (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | LifetimeTracker: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | LifetimeTracker: be33b0d89357dd2f381eb8aca636d184b538d5e9 13 | 14 | PODFILE CHECKSUM: e91e3be8fb02f5c6156c564a223f1b57ddffa7f5 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/LifetimeTracker.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LifetimeTracker", 3 | "version": "1.8.1", 4 | "summary": "Framework to visually warn you when retain cycle / leak happens.", 5 | "description": "Mini framework that can surface retain cycle issues sooner.", 6 | "homepage": "https://github.com/krzysztofzablocki/LifetimeTracker", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Krzysztof Zablocki": "krzysztof.zablocki@pixle.pl" 13 | }, 14 | "social_media_url": "http://twitter.com/merowing_", 15 | "platforms": { 16 | "ios": "8.0", 17 | "osx": "10.10" 18 | }, 19 | "source": { 20 | "git": "https://github.com/krzysztofzablocki/LifetimeTracker.git", 21 | "tag": "1.8.1" 22 | }, 23 | "ios": { 24 | "source_files": [ 25 | "Sources/*.swift", 26 | "Sources/iOS/**/*.swift" 27 | ], 28 | "resources": "Sources/Resources/**/*.{xib,storyboard}", 29 | "frameworks": [ 30 | "Foundation", 31 | "UIKit" 32 | ] 33 | }, 34 | "osx": { 35 | "source_files": "Sources/*.swift", 36 | "frameworks": [ 37 | "Foundation" 38 | ] 39 | }, 40 | "resource_bundles": { 41 | "LifetimeTracker": [ 42 | "Sources/**/*.{strings}" 43 | ] 44 | }, 45 | "swift_versions": "5.0", 46 | "swift_version": "5.0" 47 | } 48 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LifetimeTracker (1.8.1) 3 | 4 | DEPENDENCIES: 5 | - LifetimeTracker (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | LifetimeTracker: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | LifetimeTracker: be33b0d89357dd2f381eb8aca636d184b538d5e9 13 | 14 | PODFILE CHECKSUM: e91e3be8fb02f5c6156c564a223f1b57ddffa7f5 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/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 | 1.5.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-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 | 1.8.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_LifetimeTracker : NSObject 3 | @end 4 | @implementation PodsDummy_LifetimeTracker 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double LifetimeTrackerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char LifetimeTrackerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker.modulemap: -------------------------------------------------------------------------------- 1 | framework module LifetimeTracker { 2 | umbrella header "LifetimeTracker-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/LifetimeTracker.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/ResourceBundle-LifetimeTracker-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.5.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/LifetimeTracker/ResourceBundle-LifetimeTracker-LifetimeTracker-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.8.1 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## LifetimeTracker 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2017 Krzysztof Zablocki 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | Generated by CocoaPods - https://cocoapods.org 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2017 Krzysztof Zablocki 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | LifetimeTracker 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Generated by CocoaPods - https://cocoapods.org 50 | Title 51 | 52 | Type 53 | PSGroupSpecifier 54 | 55 | 56 | StringsTable 57 | Acknowledgements 58 | Title 59 | Acknowledgements 60 | 61 | 62 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | BCSYMBOLMAP_DIR="BCSymbolMaps" 23 | 24 | 25 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 26 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 27 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 28 | 29 | # Copies and strips a vendored framework 30 | install_framework() 31 | { 32 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 33 | local source="${BUILT_PRODUCTS_DIR}/$1" 34 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 35 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 36 | elif [ -r "$1" ]; then 37 | local source="$1" 38 | fi 39 | 40 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 41 | 42 | if [ -L "${source}" ]; then 43 | echo "Symlinked..." 44 | source="$(readlink "${source}")" 45 | fi 46 | 47 | if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then 48 | # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied 49 | find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do 50 | echo "Installing $f" 51 | install_bcsymbolmap "$f" "$destination" 52 | rm "$f" 53 | done 54 | rmdir "${source}/${BCSYMBOLMAP_DIR}" 55 | fi 56 | 57 | # Use filter instead of exclude so missing patterns don't throw errors. 58 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 59 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 60 | 61 | local basename 62 | basename="$(basename -s .framework "$1")" 63 | binary="${destination}/${basename}.framework/${basename}" 64 | 65 | if ! [ -r "$binary" ]; then 66 | binary="${destination}/${basename}" 67 | elif [ -L "${binary}" ]; then 68 | echo "Destination binary is symlinked..." 69 | dirname="$(dirname "${binary}")" 70 | binary="${dirname}/$(readlink "${binary}")" 71 | fi 72 | 73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 74 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 75 | strip_invalid_archs "$binary" 76 | fi 77 | 78 | # Resign the code if required by the build settings to avoid unstable apps 79 | code_sign_if_enabled "${destination}/$(basename "$1")" 80 | 81 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 82 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 83 | local swift_runtime_libs 84 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 85 | for lib in $swift_runtime_libs; do 86 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 87 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 88 | code_sign_if_enabled "${destination}/${lib}" 89 | done 90 | fi 91 | } 92 | # Copies and strips a vendored dSYM 93 | install_dsym() { 94 | local source="$1" 95 | warn_missing_arch=${2:-true} 96 | if [ -r "$source" ]; then 97 | # Copy the dSYM into the targets temp dir. 98 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 99 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 100 | 101 | local basename 102 | basename="$(basename -s .dSYM "$source")" 103 | binary_name="$(ls "$source/Contents/Resources/DWARF")" 104 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" 105 | 106 | # Strip invalid architectures from the dSYM. 107 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 108 | strip_invalid_archs "$binary" "$warn_missing_arch" 109 | fi 110 | if [[ $STRIP_BINARY_RETVAL == 0 ]]; then 111 | # Move the stripped file into its final destination. 112 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 113 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 114 | else 115 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 116 | mkdir -p "${DWARF_DSYM_FOLDER_PATH}" 117 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" 118 | fi 119 | fi 120 | } 121 | 122 | # Used as a return value for each invocation of `strip_invalid_archs` function. 123 | STRIP_BINARY_RETVAL=0 124 | 125 | # Strip invalid architectures 126 | strip_invalid_archs() { 127 | binary="$1" 128 | warn_missing_arch=${2:-true} 129 | # Get architectures for current target binary 130 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 131 | # Intersect them with the architectures we are building for 132 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 133 | # If there are no archs supported by this binary then warn the user 134 | if [[ -z "$intersected_archs" ]]; then 135 | if [[ "$warn_missing_arch" == "true" ]]; then 136 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 137 | fi 138 | STRIP_BINARY_RETVAL=1 139 | return 140 | fi 141 | stripped="" 142 | for arch in $binary_archs; do 143 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 144 | # Strip non-valid architectures in-place 145 | lipo -remove "$arch" -output "$binary" "$binary" 146 | stripped="$stripped $arch" 147 | fi 148 | done 149 | if [[ "$stripped" ]]; then 150 | echo "Stripped $binary of architectures:$stripped" 151 | fi 152 | STRIP_BINARY_RETVAL=0 153 | } 154 | 155 | # Copies the bcsymbolmap files of a vendored framework 156 | install_bcsymbolmap() { 157 | local bcsymbolmap_path="$1" 158 | local destination="${BUILT_PRODUCTS_DIR}" 159 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 160 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 161 | } 162 | 163 | # Signs a framework with the provided identity 164 | code_sign_if_enabled() { 165 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 166 | # Use the current code_sign_identity 167 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 168 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 169 | 170 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 171 | code_sign_cmd="$code_sign_cmd &" 172 | fi 173 | echo "$code_sign_cmd" 174 | eval "$code_sign_cmd" 175 | fi 176 | } 177 | 178 | if [[ "$CONFIGURATION" == "Debug" ]]; then 179 | install_framework "${BUILT_PRODUCTS_DIR}/LifetimeTracker/LifetimeTracker.framework" 180 | fi 181 | if [[ "$CONFIGURATION" == "Release" ]]; then 182 | install_framework "${BUILT_PRODUCTS_DIR}/LifetimeTracker/LifetimeTracker.framework" 183 | fi 184 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 185 | wait 186 | fi 187 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker/LifetimeTracker.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "LifetimeTracker" -framework "UIKit" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Example { 2 | umbrella header "Pods-Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker/LifetimeTracker.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "LifetimeTracker" -framework "UIKit" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Example_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Example_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_Example_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_Example_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker/LifetimeTracker.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "LifetimeTracker" -framework "UIKit" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Example_Tests { 2 | umbrella header "Pods-Example_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Example_Tests/Pods-Example_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker/LifetimeTracker.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "LifetimeTracker" -framework "UIKit" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Example/Tests/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 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import LifetimeTracker 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ExampleObjC 4 | // 5 | // Created by Krzysztof Zablocki on 17/01/2018. 6 | // Copyright © 2018 Pixle. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ExampleObjC 4 | // 5 | // Created by Krzysztof Zablocki on 17/01/2018. 6 | // Copyright © 2018 Pixle. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | @import LifetimeTracker; 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | LifetimeTrackerDashboardIntegration *dashboardIntegration = [LifetimeTrackerDashboardIntegration new]; 21 | [dashboardIntegration setVisibleWhenIssueDetected]; 22 | [dashboardIntegration useBarStyle]; 23 | [LifetimeTracker setupOnLeakDetected:^(Entry * _Nonnull entry, EntriesGroup * _Nonnull group) { 24 | NSLog(@"Leak info:%@-%@",entry.name,group.name); 25 | } onUpdate:^(NSDictionary * groups) { 26 | [dashboardIntegration refreshUIWithTrackedGroups: groups]; 27 | }]; 28 | return YES; 29 | } 30 | 31 | 32 | - (void)applicationWillResignActive:(UIApplication *)application { 33 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 34 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 35 | } 36 | 37 | 38 | - (void)applicationDidEnterBackground:(UIApplication *)application { 39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | } 42 | 43 | 44 | - (void)applicationWillEnterForeground:(UIApplication *)application { 45 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 46 | } 47 | 48 | 49 | - (void)applicationDidBecomeActive:(UIApplication *)application { 50 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 51 | } 52 | 53 | 54 | - (void)applicationWillTerminate:(UIApplication *)application { 55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 56 | } 57 | 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ExampleObjC 4 | // 5 | // Created by Krzysztof Zablocki on 17/01/2018. 6 | // Copyright © 2018 Pixle. All rights reserved. 7 | // 8 | 9 | #import 10 | @import LifetimeTracker; 11 | 12 | @interface ViewController : UIViewController 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ExampleObjC 4 | // 5 | // Created by Krzysztof Zablocki on 17/01/2018. 6 | // Copyright © 2018 Pixle. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | @property (nonatomic, retain) NSMutableArray* leakStorage; 13 | @end 14 | 15 | @implementation ViewController 16 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 17 | { 18 | self = [super initWithCoder:aDecoder]; 19 | if (self) { 20 | self.leakStorage = [NSMutableArray new]; 21 | } 22 | 23 | [self trackLifetime]; 24 | return self; 25 | } 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | if (self) { 31 | self.leakStorage = [NSMutableArray new]; 32 | } 33 | [self trackLifetime]; 34 | return self; 35 | } 36 | 37 | + (LifetimeConfiguration *)lifetimeConfiguration { 38 | return [[LifetimeConfiguration alloc] initWithMaxCount:1 groupName:@"VC"]; 39 | } 40 | 41 | - (IBAction)createLeaks:(id)sender { 42 | [self.leakStorage addObject:[ViewController new]]; 43 | [self.leakStorage addObject:[ViewController new]]; 44 | } 45 | 46 | - (IBAction)removeLeaks:(id)sender { 47 | [self.leakStorage removeAllObjects]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /ExampleObjC/ExampleObjC/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ExampleObjC 4 | // 5 | // Created by Krzysztof Zablocki on 17/01/2018. 6 | // Copyright © 2018 Pixle. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ExampleObjC/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | use_frameworks! 4 | 5 | target 'ExampleObjC' do 6 | pod 'LifetimeTracker', :path => '../' 7 | end 8 | -------------------------------------------------------------------------------- /ExampleObjC/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LifetimeTracker (1.7.1) 3 | 4 | DEPENDENCIES: 5 | - LifetimeTracker (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | LifetimeTracker: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | LifetimeTracker: e364d6b8f4de0cd3bcd278b33a80bd1660c6fcaf 13 | 14 | PODFILE CHECKSUM: d7142788e4e191a028bf1f058d7f8e372fac6645 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Local Podspecs/LifetimeTracker.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LifetimeTracker", 3 | "version": "1.7.1", 4 | "summary": "Framework to visually warn you when retain cycle / leak happens.", 5 | "description": "Mini framework that can surface retain cycle issues sooner.", 6 | "homepage": "https://github.com/krzysztofzablocki/LifetimeTracker", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Krzysztof Zablocki": "krzysztof.zablocki@pixle.pl" 13 | }, 14 | "social_media_url": "http://twitter.com/merowing_", 15 | "platforms": { 16 | "ios": "8.0", 17 | "osx": "10.10" 18 | }, 19 | "source": { 20 | "git": "https://github.com/krzysztofzablocki/LifetimeTracker.git", 21 | "tag": "1.7.1" 22 | }, 23 | "ios": { 24 | "source_files": [ 25 | "Sources/*.swift", 26 | "Sources/iOS/**/*.swift" 27 | ], 28 | "resources": "Sources/iOS/*.{xib,storyboard}", 29 | "frameworks": [ 30 | "Foundation", 31 | "UIKit" 32 | ] 33 | }, 34 | "osx": { 35 | "source_files": "Sources/*.swift", 36 | "frameworks": [ 37 | "Foundation" 38 | ] 39 | }, 40 | "resource_bundles": { 41 | "LifetimeTracker": [ 42 | "Sources/**/*.{strings}" 43 | ] 44 | }, 45 | "swift_versions": "5.0", 46 | "swift_version": "5.0" 47 | } 48 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - LifetimeTracker (1.7.1) 3 | 4 | DEPENDENCIES: 5 | - LifetimeTracker (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | LifetimeTracker: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | LifetimeTracker: e364d6b8f4de0cd3bcd278b33a80bd1660c6fcaf 13 | 14 | PODFILE CHECKSUM: d7142788e4e191a028bf1f058d7f8e372fac6645 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/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 | 1.3.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-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 | 1.7.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_LifetimeTracker : NSObject 3 | @end 4 | @implementation PodsDummy_LifetimeTracker 5 | @end 6 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/LifetimeTracker-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double LifetimeTrackerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char LifetimeTrackerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/LifetimeTracker.modulemap: -------------------------------------------------------------------------------- 1 | framework module LifetimeTracker { 2 | umbrella header "LifetimeTracker-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/LifetimeTracker.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/ResourceBundle-LifetimeTracker-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.3.3 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/LifetimeTracker/ResourceBundle-LifetimeTracker-LifetimeTracker-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.7.1 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## LifetimeTracker 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2017 Krzysztof Zablocki 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | Generated by CocoaPods - https://cocoapods.org 30 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2017 Krzysztof Zablocki 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | LifetimeTracker 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Generated by CocoaPods - https://cocoapods.org 50 | Title 51 | 52 | Type 53 | PSGroupSpecifier 54 | 55 | 56 | StringsTable 57 | Acknowledgements 58 | Title 59 | Acknowledgements 60 | 61 | 62 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ExampleObjC : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ExampleObjC 5 | @end 6 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/LifetimeTracker/LifetimeTracker.framework" 165 | fi 166 | if [[ "$CONFIGURATION" == "Release" ]]; then 167 | install_framework "${BUILT_PRODUCTS_DIR}/LifetimeTracker/LifetimeTracker.framework" 168 | fi 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | wait 171 | fi 172 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ExampleObjCVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ExampleObjCVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC.debug.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker/LifetimeTracker.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "LifetimeTracker" -framework "UIKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ExampleObjC { 2 | umbrella header "Pods-ExampleObjC-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /ExampleObjC/Pods/Target Support Files/Pods-ExampleObjC/Pods-ExampleObjC.release.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LifetimeTracker/LifetimeTracker.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "LifetimeTracker" -framework "UIKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Funding.yml: -------------------------------------------------------------------------------- 1 | custom: https://www.merowing.info/membership 2 | github: krzysztofzablocki 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Krzysztof Zablocki 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 | 23 | -------------------------------------------------------------------------------- /LifetimeTracker.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "LifetimeTracker" 3 | s.version = "1.8.3" 4 | s.summary = "Framework to visually warn you when retain cycle / leak happens." 5 | s.description = <<-DESC 6 | Mini framework that can surface retain cycle issues sooner. 7 | DESC 8 | s.homepage = "https://github.com/krzysztofzablocki/LifetimeTracker" 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | s.author = { "Krzysztof Zablocki" => "krzysztof.zablocki@pixle.pl" } 11 | s.social_media_url = "http://twitter.com/merowing_" 12 | s.ios.deployment_target = "12.0" 13 | s.osx.deployment_target = '10.13' 14 | s.source = { :git => "https://github.com/krzysztofzablocki/LifetimeTracker.git", :tag => s.version.to_s } 15 | s.ios.source_files = "Sources/*.swift", "Sources/iOS/**/*.swift" 16 | s.macos.source_files = "Sources/*.swift" 17 | s.ios.resources = "Sources/Resources/**/*.{xib,storyboard}" 18 | s.resource_bundle = { "LifetimeTracker" => ["Sources/**/*.{strings}"] } 19 | s.ios.frameworks = ["Foundation", "UIKit"] 20 | s.macos.frameworks = ["Foundation"] 21 | s.swift_version = "5.0" 22 | end 23 | -------------------------------------------------------------------------------- /LifetimeTracker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LifetimeTracker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LifetimeTracker.xcodeproj/xcshareddata/xcschemes/LifetimeTracker-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /LifetimeTracker.xcodeproj/xcshareddata/xcschemes/LifetimeTracker-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /LifetimeTracker.xcodeproj/xcshareddata/xcschemes/LifetimeTracker-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /LifetimeTracker.xcodeproj/xcshareddata/xcschemes/LifetimeTracker-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 44 | 45 | 51 | 52 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "LifetimeTracker", 7 | defaultLocalization: "en", 8 | products: [ 9 | .library( 10 | name: "LifetimeTracker", 11 | targets: ["LifetimeTracker"] 12 | ), 13 | ], 14 | targets: [ 15 | .target( 16 | name: "LifetimeTracker", 17 | path: "Sources", 18 | resources: [ 19 | .process("Resources"), 20 | .process("Localizable.strings", localization: .default), 21 | ] 22 | ) 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LifetimeTracker 2 | 3 | 4 | | ![Demo (bar)](Resources/demo-bar.gif) | | ![Demo (circular)](Resources/demo-circular.gif) | 5 | |:--:| :--: | :--: | 6 | | Bar style | | Circular style | 7 | 8 | LifetimeTracker can surface retain cycle / memory issues right as you develop your application, and it will surface them to you immediately, so you can find them with more ease. 9 | 10 | Instruments and Memory Graph Debugger are great, but too many times, developers forget to check for issues as they close the feature implementation. 11 | 12 | If you use those tools sporadically many of the issues they surface will require you to investigate the cause and cost you a lot of time in the process. 13 | 14 | Other tools like [FBRetainCycleDetector](https://github.com/facebook/FBRetainCycleDetector) rely on objc runtime magic to find the problems, but that means they can't really be used for pure Swift classes. This small tool simply focuses on tracking lifetime of objects which means that it can be used in both Objective-C and Swift codebases, and it doesn't rely on any complex or automatic magic behaviour. 15 | 16 | [**If you'd like to support my work and improve your engineering workflows, check out my SwiftyStack course**](https://www.swiftystack.com/) 17 | 18 | ## Installation 19 | 20 | ### CocoaPods 21 | 22 | Add `pod 'LifetimeTracker'` to your Podfile. 23 | 24 | ### Carthage 25 | 26 | Add `github "krzysztofzablocki/LifetimeTracker"` to your Cartfile. 27 | 28 | ### Swift Package Manager 29 | 30 | Add `LifetimeTracker"` to the dependencies value of your Package.swift. 31 | 32 | ```swift 33 | dependencies: [ 34 | .package(url: "https://github.com/krzysztofzablocki/LifetimeTracker.git", .upToNextMajor(from: "1.8.0")) 35 | ] 36 | ``` 37 | 38 | ## Integration 39 | 40 | To Integrate visual notifications simply add following line at the start of `AppDelegate(didFinishLaunchingWithOptions:)` or if you are using iOS 13+ SceneDelegates in `scene(willConnectTo:options:)`. 41 | 42 | Swift: 43 | 44 | ```swift 45 | #if DEBUG 46 | LifetimeTracker.setup( 47 | onUpdate: LifetimeTrackerDashboardIntegration( 48 | visibility: .alwaysVisible, 49 | style: .bar, 50 | textColorForNoIssues: .systemGreen, 51 | textColorForLeakDetected: .systemRed 52 | ).refreshUI 53 | ) 54 | #endif 55 | ``` 56 | 57 | Objective-C: 58 | 59 | ```objc 60 | LifetimeTrackerDashboardIntegration *dashboardIntegration = [LifetimeTrackerDashboardIntegration new]; 61 | [dashboardIntegration setVisibleWhenIssueDetected]; 62 | [dashboardIntegration useBarStyle]; 63 | [LifetimeTracker setupOnUpdate:^(NSDictionary * groups) { 64 | [dashboardIntegration refreshUIWithTrackedGroups: groups]; 65 | }]; 66 | ``` 67 | 68 | You can control when the dashboard is visible: `alwaysVisible`, `alwaysHidden`, or `visibleWithIssuesDetected`. 69 | 70 | There are two styles available. A overlay bar view which shows the detailed list of issues directly on the screen or a circular view which displays only the amount of issues and opens the detailed list as modal view controller. 71 | 72 | ## Tracking key actors 73 | 74 | Usually, you want to use LifetimeTracker to track only key actors in your app, like ViewModels / Controllers etc. When you have more than `maxCount` items alive, the tracker will let you know. 75 | 76 | ### Swift 77 | 78 | You conform to `LifetimeTrackable` and call `trackLifetime()` at the end of your init functions: 79 | 80 | ```swift 81 | class SectionFrontViewController: UIViewController, LifetimeTrackable { 82 | class var lifetimeConfiguration: LifetimeConfiguration { 83 | return LifetimeConfiguration(maxCount: 1, groupName: "VC") 84 | } 85 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 86 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 87 | /// ... 88 | trackLifetime() 89 | } 90 | } 91 | ``` 92 | 93 | ### Objective-C 94 | 95 | You conform to `LifetimeTrackable` and call `[self trackLifetime]` at the end of your init functions: 96 | 97 | ```objc 98 | @import LifetimeTracker; 99 | 100 | @interface SectionFrontViewController() 101 | 102 | @implementation SectionFrontViewController 103 | 104 | +(LifetimeConfiguration *)lifetimeConfiguration 105 | { 106 | return [[LifetimeConfiguration alloc] initWithMaxCount:1 groupName:@"VC"]; 107 | } 108 | 109 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 110 | { 111 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 112 | if (self) { 113 | /// … 114 | [self trackLifetime]; 115 | } 116 | return self; 117 | } 118 | @end 119 | ``` 120 | 121 | ## Integrating with [Danger](https://danger.systems) 122 | 123 | If you are using Danger, you can use it to add both checkboxes to each PR to ensure people have verified no retain cycles were created but also to inform you when someone forgets to call `trackLifetime()` function. 124 | 125 | ```ruby 126 | # 127 | # ** FILE CHECKS ** 128 | # Checks for certain rules and warns if needed. 129 | # Some rules can be disabled by using // danger:disable rule_name 130 | # 131 | # Rules: 132 | # - Check if the modified file is a View and doesn't implement LifetimeTrackable (lifetime_tracking) 133 | 134 | # Sometimes an added file is also counted as modified. We want the files to be checked only once. 135 | files_to_check = (git.modified_files + git.added_files).uniq 136 | (files_to_check - %w(Dangerfile)).each do |file| 137 | next unless File.file?(file) 138 | # Only check inside swift files 139 | next unless File.extname(file).include?(".swift") 140 | 141 | # Will be used to check if we're inside a comment block. 142 | is_comment_block = false 143 | 144 | # Collects all disabled rules for this file. 145 | disabled_rules = [] 146 | 147 | filelines = File.readlines(file) 148 | filelines.each_with_index do |line, index| 149 | if is_comment_block 150 | if line.include?("*/") 151 | is_comment_block = false 152 | end 153 | elsif line.include?("/*") 154 | is_comment_block = true 155 | elsif line.include?("danger:disable") 156 | rule_to_disable = line.split.last 157 | disabled_rules.push(rule_to_disable) 158 | else 159 | # Start our custom line checks 160 | # e.g. you could do something like check for methods that only call the super class' method 161 | #if line.include?("override") and line.include?("func") and filelines[index+1].include?("super") and filelines[index+2].include?("}") 162 | # warn("Override methods which only call super can be removed", file: file, line: index+3) 163 | #end 164 | end 165 | end 166 | 167 | # Only continue checks for Lifetime Trackable types 168 | next unless (File.basename(file).include?("ViewModel") or File.basename(file).include?("ViewController") or File.basename(file).include?("View.swift")) and !File.basename(file).include?("Node") and !File.basename(file).include?("Tests") and !File.basename(file).include?("FlowCoordinator") 169 | 170 | if disabled_rules.include?("lifetime_tracking") == false 171 | if File.readlines(file).grep(/LifetimeTrackable/).any? 172 | fail("You forgot to call trackLifetime() from your initializers in " + File.basename(file, ".*") + " (lifetime_tracking)") unless File.readlines(file).grep(/trackLifetime()/).any? 173 | else 174 | warn("Please add support for LifetimeTrackable to " + File.basename(file, ".*") + " . (lifetime_tracking)") 175 | end 176 | markdown("- [ ] I've verified that showing and hiding " + File.basename(file, ".*") + " doesn't surface any [LifetimeTracker](https://github.com/krzysztofzablocki/LifetimeTracker) issues") 177 | end 178 | 179 | end 180 | ``` 181 | 182 | ## Surface last notification from the stack 183 | 184 | Sometimes it is useful to get information about last retain cycle in order to log it to external sources such as analytics/trackers. In order to do that we can update initial configuration with `onLeakDetected`: 185 | 186 | ```objc 187 | [LifetimeTracker setupOnLeakDetected:^(Entry * entry, EntriesGroup * group) { 188 | NSLog(@"POSSIBLE LEAK ALERT: %@ - current count %li, max count %li", entry.name, (long)entry.count, (long)entry.maxCount); 189 | } onUpdate:^(NSDictionary * groups) { 190 | [dashboardIntegration refreshUIWithTrackedGroups: groups]; 191 | }]; 192 | ``` 193 | 194 | ```swift 195 | LifetimeTracker.setup(onLeakDetected: { entity, _ in 196 | log.warning("POSSIBLE LEAK ALERT: \(entity.name) - current count: \(entity.count), max count: \(entity.maxCount)") 197 | }, onUpdate: LifetimeTrackerDashboardIntegration(visibility: .alwaysVisible, style: .bar).refreshUI) 198 | ``` 199 | 200 | ## Group tracked objects 201 | 202 | You can group tracked objects together. `maxCount` of a group will be calculated by `maxCount` of all members per default. However, you can override it and provide a separate value to the group with `groupMaxCount`. 203 | 204 | You may want to do this when you have a set of subclasses which can appear x times each, but in total only less than the sum of all subclasses: 205 | 206 | ```swift 207 | // DetailPage: UIViewController 208 | 209 | // VideoDetailPage: DetailItem 210 | LifetimeConfiguration(maxCount: 3, groupName: "Detail Page") 211 | 212 | // ImageDetailPage: DetailItem 213 | LifetimeConfiguration(maxCount: 3, groupName: "Detail Page") 214 | 215 | => Group warning if 7 DetailPage objects are alive 216 | 217 | // VideoDetailPage: DetailItem 218 | LifetimeConfiguration(maxCount: 3, groupName: "Detail Page", groupMaxCount: 3) 219 | 220 | // ImageDetailPage: DetailItem 221 | LifetimeConfiguration(maxCount: 3, groupName: "Detail Page", groupMaxCount: 3) 222 | 223 | => Group warning if 4 DetailPage object are alive 224 | 225 | ``` 226 | 227 | ## Writing integration tests for memory leaks 228 | 229 | You can access the summary label using accessibility identifier `LifetimeTracker.summaryLabel`, which allows you to write integration tests that end up with looking up whether any issues were found. 230 | 231 | ## License 232 | 233 | LifetimeTracker is available under the MIT license. See [LICENSE](LICENSE) for more information. 234 | 235 | ## Attributions 236 | 237 | I've used [SwiftPlate](https://github.com/JohnSundell/SwiftPlate) to generate xcodeproj compatible with CocoaPods and Carthage. 238 | -------------------------------------------------------------------------------- /Resources/demo-bar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krzysztofzablocki/LifetimeTracker/30d347811bfcf2f37ef0c2ed7e866269c4f836ad/Resources/demo-bar.gif -------------------------------------------------------------------------------- /Resources/demo-circular.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krzysztofzablocki/LifetimeTracker/30d347811bfcf2f37ef0c2ed7e866269c4f836ad/Resources/demo-circular.gif -------------------------------------------------------------------------------- /Sources/Bundle+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Leo Tsuchiya on 6/6/21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | 12 | /// Returns a package manager appropriate `Bundle`. 13 | static var resolvedBundle: Bundle { 14 | #if SWIFT_PACKAGE 15 | return Bundle.module 16 | #else 17 | return Bundle(for: LifetimeTracker.self) 18 | #endif 19 | } 20 | static var stringBundle: Bundle { 21 | #if SWIFT_PACKAGE 22 | return Bundle.module 23 | #else 24 | let mainBundle = Bundle(for: LifetimeTracker.self) 25 | if let podBundlePath = mainBundle.path(forResource: "LifetimeTracker", ofType: "bundle") { 26 | return Bundle(path: podBundlePath) ?? mainBundle 27 | } 28 | return mainBundle 29 | #endif 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Hans Seiffert on 23.10.17. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal struct Constants { 12 | 13 | struct Identifier { 14 | 15 | struct EntryGroup { 16 | static let none = "lifetimetracker.nogroup.identifier" 17 | } 18 | 19 | struct Reuse { 20 | static let dashboardCell = "lifetimeTrackerDashboardTableViewCell" 21 | static let dashboardHeader = "lifetimeTrackerDashboardHeaderViewCell" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/DeallocTracker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeallocTracker.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Krzysztof Zablocki on 9/25/17. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | fileprivate final class DeallocTracker { 12 | let onDealloc: () -> Void 13 | 14 | init(onDealloc: @escaping () -> Void) { 15 | self.onDealloc = onDealloc 16 | } 17 | 18 | deinit { 19 | onDealloc() 20 | } 21 | } 22 | 23 | /// Executes action upon deallocation of owner 24 | /// 25 | /// - Parameters: 26 | /// - owner: Owner to track. 27 | /// - closure: Closure to execute. 28 | internal func onDealloc(of owner: Any, closure: @escaping () -> Void) { 29 | let tracker = DeallocTracker(onDealloc: closure) 30 | objc_setAssociatedObject(owner, getID(tracker), tracker, .OBJC_ASSOCIATION_RETAIN) 31 | } 32 | 33 | // https://github.com/atrick/swift-evolution/blob/diagnose-implicit-raw-bitwise/proposals/nnnn-implicit-raw-bitwise-conversion.md#associated-object-string-keys 34 | private func getID(_ object: AnyObject) -> UnsafeRawPointer { 35 | return UnsafeRawPointer(Unmanaged.passUnretained(object).toOpaque()) 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Hans Seiffert on 23.10.17. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal extension String { 12 | 13 | var lt_localized: String { 14 | Bundle.stringBundle.localizedString(forKey: self, value: self, table: nil) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | LifetimeTracker 4 | 5 | Created by Hans Seiffert on 23.10.17. 6 | 7 | */ 8 | 9 | "popover.dasboard.title" = "LifetimeTracker"; 10 | 11 | "dashboard.header.issue.description.leakDetected" = "Detected"; 12 | 13 | "dashboard.header.issue.description.noIssues" = "No issues detected"; 14 | 15 | "dashboard.sectionHeader.title.noGroup" = "Others"; 16 | 17 | "macCount.notSpecified" = "∞"; 18 | 19 | "word.leak" = "leak"; 20 | 21 | "word.leaks" = "leaks"; 22 | 23 | "settings" = "Settings"; 24 | 25 | "settings.cancel" = "Cancel"; 26 | 27 | "settings.hide.title" = "Hide LifetimeTracker"; 28 | 29 | "settings.hide.sheet.title" = "Hide until ..."; 30 | 31 | "settings.hide.sheet.untilMoreIssue" = "more issues are detected"; 32 | 33 | "settings.hide.sheet.untilNewType" = "new issue types are detected"; 34 | 35 | "settings.hide.sheet.untilRestart" = "the app was restarted"; 36 | -------------------------------------------------------------------------------- /Sources/Resources/DashboardTableView.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Sources/Resources/DashboardTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Sources/Resources/DashboardTableViewHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Sources/iOS/SettingsManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsManager.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Thanh Duc Do on 23.08.18. 6 | // Copyright © 2018 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct SettingsManager { 12 | 13 | // On iPhone, this has no effect if the alert has preferredStyle: .actionSheet. 14 | // On iPad, this creates a root-less popover which mimics the appearance of an actionSheet 15 | // without requiring a sourceView or barButton. 16 | private static func createRootlessPopover(centeredOn view: UIView, alert: UIAlertController) { 17 | if let popoverController = alert.popoverPresentationController { 18 | popoverController.sourceView = view 19 | popoverController.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0) 20 | popoverController.permittedArrowDirections = [] 21 | } 22 | } 23 | 24 | static func showSettingsActionSheet(on viewController: UIViewController, completionHandler: @escaping (HideOption) -> Void) { 25 | let alert = UIAlertController(title: "settings".lt_localized, message: nil, preferredStyle: .actionSheet) 26 | alert.addAction(UIAlertAction(title: "settings.hide.title".lt_localized, style: .default, handler: { (action: UIAlertAction) in 27 | let alert = UIAlertController(title: "settings.hide.sheet.title".lt_localized, message: nil, preferredStyle: .actionSheet) 28 | alert.addAction(UIAlertAction(title: "settings.hide.sheet.untilMoreIssue".lt_localized, style: .default, handler: { (action: UIAlertAction) in 29 | completionHandler(.untilMoreIssue) 30 | })) 31 | alert.addAction(UIAlertAction(title: "settings.hide.sheet.untilNewType".lt_localized, style: .default, handler: { (action: UIAlertAction) in 32 | completionHandler(.untilNewIssueType) 33 | })) 34 | alert.addAction(UIAlertAction(title: "settings.hide.sheet.untilRestart".lt_localized, style: .default, handler: { (action: UIAlertAction) in 35 | completionHandler(.always) 36 | })) 37 | alert.addAction(UIAlertAction(title: "settings.cancel".lt_localized, style: .cancel, handler: { (action: UIAlertAction) in 38 | completionHandler(.none) 39 | })) 40 | createRootlessPopover(centeredOn: viewController.view, alert: alert) 41 | viewController.present(alert, animated: true, completion: nil) 42 | })) 43 | alert.addAction(UIAlertAction(title: "settings.cancel".lt_localized, style: .cancel, handler: { (action: UIAlertAction) in 44 | completionHandler(.none) 45 | })) 46 | 47 | createRootlessPopover(centeredOn: viewController.view, alert: alert) 48 | 49 | viewController.present(alert, animated: true, completion: nil) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/iOS/UI/BarDashboard/BarDashboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarDashboardViewController.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Krzysztof Zablocki on 9/25/17. 6 | // 7 | 8 | import UIKit 9 | 10 | struct BarDashboardViewModel { 11 | let leaksCount: Int 12 | let summary: NSAttributedString 13 | let sections: [GroupModel] 14 | let textColorForNoIssues: UIColor 15 | let textColorForLeakDetected: UIColor 16 | 17 | init( 18 | leaksCount: Int = 0, 19 | summary: NSAttributedString = NSAttributedString(), 20 | sections: [GroupModel] = [], 21 | textColorForNoIssues: UIColor = .systemGreen, 22 | textColorForLeakDetected: UIColor = .systemRed 23 | ) { 24 | self.leaksCount = leaksCount 25 | self.summary = summary 26 | self.sections = sections 27 | self.textColorForNoIssues = textColorForNoIssues 28 | self.textColorForLeakDetected = textColorForLeakDetected 29 | } 30 | } 31 | 32 | final class BarDashboardViewController: UIViewController, LifetimeTrackerViewable { 33 | 34 | enum State { 35 | case open 36 | case closed 37 | 38 | var opposite: State { 39 | switch self { 40 | case .open: return .closed 41 | case .closed: return .open 42 | } 43 | } 44 | } 45 | 46 | enum Edge: Int { 47 | case top 48 | case bottom 49 | } 50 | 51 | class func makeFromNib() -> UIViewController & LifetimeTrackerViewable { 52 | let storyboard = UIStoryboard(name: Constants.Storyboard.barDashboard.name, bundle: .resolvedBundle) 53 | return storyboard.instantiateViewController(withIdentifier: String(describing: self)) as! BarDashboardViewController 54 | } 55 | 56 | private var state: State = .closed { 57 | didSet { clampDragOffset() } 58 | } 59 | 60 | private var hideOption: HideOption = .none { 61 | didSet { 62 | if hideOption != .none { 63 | view.window?.isHidden = true 64 | } 65 | } 66 | } 67 | 68 | fileprivate var dashboardViewModel = BarDashboardViewModel() 69 | @IBOutlet private var tableViewController: DashboardTableViewController? 70 | @IBOutlet private var summaryLabel: UILabel? 71 | @IBOutlet private weak var barView: UIView! 72 | @IBOutlet private weak var headerView: UIView? 73 | 74 | public var edge: Edge = .bottom 75 | 76 | private let closedHeight: CGFloat = Constants.Layout.Dashboard.headerHeight 77 | private var originalOffset: CGFloat = 0 78 | private var dragOffset: CGFloat = 0 { 79 | didSet { relayout() } 80 | } 81 | private var offsetForCloseJumpBack: CGFloat = 0 82 | private var currentScreenSize: CGSize = CGSize.zero 83 | private var fullScreen: Bool = false 84 | 85 | override func viewDidLoad() { 86 | super.viewDidLoad() 87 | 88 | view.translatesAutoresizingMaskIntoConstraints = false 89 | 90 | addTapGestureRecognizer() 91 | addPanGestureRecognizer() 92 | dragOffset = maximumYPosition 93 | } 94 | 95 | override var prefersStatusBarHidden: Bool { 96 | return false 97 | } 98 | 99 | func update(with vm: BarDashboardViewModel) { 100 | summaryLabel?.attributedText = vm.summary 101 | 102 | if hideOption.shouldUIBeShown(oldModel: dashboardViewModel, newModel: vm) { 103 | view.isHidden = false 104 | hideOption = .none 105 | } 106 | 107 | dashboardViewModel = vm 108 | 109 | tableViewController?.update(dashboardViewModel: vm) 110 | 111 | // Update the drag offset as the height might increase. The offset has to be decreased in this case 112 | if (dragOffset + heightToShow) > maximumHeight { 113 | dragOffset = maximumHeight - heightToShow 114 | offsetForCloseJumpBack = maximumHeight - closedHeight 115 | } 116 | 117 | relayout() 118 | } 119 | 120 | override func viewDidLayoutSubviews() { 121 | super.viewDidLayoutSubviews() 122 | 123 | if currentScreenSize != UIScreen.main.bounds.size { 124 | dragOffset = maximumYPosition 125 | offsetForCloseJumpBack = maximumHeight - closedHeight 126 | } 127 | currentScreenSize = UIScreen.main.bounds.size 128 | 129 | relayout() 130 | } 131 | 132 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 133 | if segue.identifier == Constants.Segue.embedDashboardTableView.identifier { 134 | tableViewController = segue.destination as? DashboardTableViewController 135 | tableViewController?.update(dashboardViewModel: dashboardViewModel) 136 | } 137 | } 138 | 139 | // MARK: - Layout 140 | 141 | private var heightToShow: CGFloat { 142 | var height = CGFloat(0) 143 | switch state { 144 | case .closed: 145 | height = closedHeight 146 | case .open: 147 | height = heightToFitTableView 148 | } 149 | return min(maximumHeight, height) 150 | } 151 | 152 | private var minimumYPosition: CGFloat { 153 | if #available(iOS 11, *) { 154 | return view.safeAreaInsets.top 155 | } else { 156 | return 0.0 157 | } 158 | } 159 | 160 | private var maximumHeight: CGFloat { 161 | if #available(iOS 11, *) { 162 | return UIScreen.main.bounds.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom 163 | } else { 164 | return UIScreen.main.bounds.height 165 | } 166 | } 167 | 168 | private var maximumYPosition: CGFloat { 169 | if #available(iOS 11, *) { 170 | return maximumHeight - heightToShow - view.safeAreaInsets.bottom + view.safeAreaInsets.top 171 | } else { 172 | return maximumHeight - heightToShow 173 | } 174 | } 175 | 176 | private var heightToFitTableView: CGFloat { 177 | let size = tableViewController?.contentSize ?? CGSize.zero 178 | return max(Constants.Layout.Dashboard.minTotalHeight, size.height + closedHeight) 179 | } 180 | 181 | private var layoutWidth: CGFloat { return UIScreen.main.bounds.width } 182 | 183 | private func relayout() { 184 | guard let window = view.window else { return } 185 | 186 | // Prevent black areas during device orientation 187 | window.clipsToBounds = true 188 | window.translatesAutoresizingMaskIntoConstraints = true 189 | window.frame = CGRect(x: 0, y: fullScreen ? 0 : dragOffset, width: UIScreen.main.bounds.width, height: fullScreen ? UIScreen.main.bounds.height : heightToShow) 190 | view.layoutIfNeeded() 191 | } 192 | 193 | // MARK: - Expand / collapse 194 | 195 | func addTapGestureRecognizer() { 196 | let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(headerTapped)) 197 | headerView?.addGestureRecognizer(tapGestureRecognizer) 198 | } 199 | 200 | @objc func headerTapped() { 201 | if state == .closed { 202 | offsetForCloseJumpBack = dragOffset 203 | } 204 | state = state.opposite 205 | 206 | if state == .closed && offsetForCloseJumpBack == maximumYPosition { 207 | dragOffset = offsetForCloseJumpBack 208 | } 209 | 210 | UIView.animateKeyframes(withDuration: Constants.Layout.animationDuration, delay: 0, options: [.beginFromCurrentState, .calculationModeCubicPaced] , animations: { 211 | self.relayout() 212 | }, completion: nil) 213 | } 214 | 215 | // MARK: - Settings 216 | 217 | @IBAction private func settingsButtonTapped(_ sender: UIButton) { 218 | guard let originalWindowFrame = view.window?.frame.origin else { 219 | return 220 | } 221 | let originalBarFrame = barView.frame 222 | barView.translatesAutoresizingMaskIntoConstraints = true 223 | barView.frame = CGRect(x: originalWindowFrame.x, y: originalWindowFrame.y, width: originalBarFrame.width, height: originalBarFrame.height) 224 | fullScreen = true 225 | relayout() 226 | SettingsManager.showSettingsActionSheet(on: self, completionHandler: { [weak self] (selectedOption: HideOption) in 227 | self?.hideOption = selectedOption 228 | self?.barView.translatesAutoresizingMaskIntoConstraints = false 229 | self?.fullScreen = false 230 | self?.relayout() 231 | }) 232 | } 233 | 234 | // MARK: Panning 235 | 236 | func addPanGestureRecognizer() { 237 | let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(toolbarPanned)) 238 | view.addGestureRecognizer(panGestureRecognizer) 239 | } 240 | 241 | @objc func toolbarPanned(_ gestureRecognizer: UIPanGestureRecognizer) { 242 | switch gestureRecognizer.state { 243 | case .began: 244 | originalOffset = dragOffset 245 | case .changed: 246 | let translation = gestureRecognizer.translation(in: self.view) 247 | dragOffset = originalOffset + translation.y 248 | clampDragOffset() 249 | if state == .open { 250 | offsetForCloseJumpBack = dragOffset 251 | } 252 | default: break 253 | } 254 | } 255 | 256 | func clampDragOffset() { 257 | if dragOffset < minimumYPosition { 258 | dragOffset = minimumYPosition 259 | } else if dragOffset > maximumYPosition { 260 | dragOffset = maximumYPosition 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Sources/iOS/UI/Constants+UIKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Hans Seiffert on 09.11.17. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension Constants { 12 | 13 | struct Layout { 14 | static let animationDuration: TimeInterval = 0.3 15 | 16 | struct Dashboard { 17 | static let headerHeight: CGFloat = 44.0 18 | static let cellHeight: CGFloat = 44 19 | static let minTotalHeight: CGFloat = headerHeight + cellHeight 20 | static let sectionHeaderHeight: CGFloat = 30 21 | } 22 | } 23 | 24 | enum Segue { 25 | case embedDashboardTableView 26 | 27 | var identifier: String { 28 | switch self { 29 | case .embedDashboardTableView: return "embedDashboardTableViewController" 30 | } 31 | } 32 | } 33 | 34 | enum Storyboard { 35 | case circularDashboard 36 | case barDashboard 37 | 38 | var name: String { 39 | switch self { 40 | case .circularDashboard: return "CircularDashboard" 41 | case .barDashboard: return "BarDashboard" 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/iOS/UI/DashboardTableView/DashboardTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DashboardTableViewCell.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Hans Seiffert on 21.10.17. 6 | // 7 | 8 | import UIKit 9 | 10 | class DashboardTableViewCell: UITableViewCell { 11 | 12 | @IBOutlet private weak var groupIndicatorView: UIView! 13 | @IBOutlet private weak var classIndicatorView: UIView! 14 | @IBOutlet private weak var descriptionLabel: UILabel! 15 | 16 | class var nib: UINib { 17 | return UINib(nibName: String(describing: self), bundle: .resolvedBundle) 18 | } 19 | 20 | override func prepareForReuse() { 21 | super.prepareForReuse() 22 | groupIndicatorView.backgroundColor = .clear 23 | classIndicatorView.backgroundColor = .clear 24 | descriptionLabel.text = nil 25 | } 26 | 27 | func setup(groupColor: UIColor, classColor: UIColor, description: String) { 28 | groupIndicatorView.backgroundColor = groupColor 29 | classIndicatorView.backgroundColor = classColor 30 | descriptionLabel.text = description 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/iOS/UI/DashboardTableView/DashboardTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DashboardTableViewController.swift 3 | // LifetimeTracker-iOS 4 | // 5 | // Created by Hans Seiffert on 18.03.18. 6 | // Copyright © 2018 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DashboardTableViewController: UIViewController { 12 | 13 | @IBOutlet weak var tableView: UITableView? 14 | 15 | fileprivate var dashboardViewModel = BarDashboardViewModel() 16 | 17 | var contentSize: CGSize { 18 | return tableView?.contentSize ?? CGSize.zero 19 | } 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | tableView?.register(DashboardTableViewHeaderView.lt_nibInOwnBundle, forHeaderFooterViewReuseIdentifier: Constants.Identifier.Reuse.dashboardHeader) 25 | tableView?.register(DashboardTableViewCell.lt_nibInOwnBundle, forCellReuseIdentifier: Constants.Identifier.Reuse.dashboardCell) 26 | 27 | tableView?.scrollsToTop = false 28 | } 29 | 30 | func update(dashboardViewModel: BarDashboardViewModel) { 31 | self.dashboardViewModel = dashboardViewModel 32 | 33 | tableView?.reloadData() 34 | tableView?.layoutIfNeeded() 35 | } 36 | } 37 | 38 | // MARK: - UITableViewDataSource 39 | 40 | extension DashboardTableViewController: UITableViewDataSource { 41 | 42 | func numberOfSections(in tableView: UITableView) -> Int { 43 | return dashboardViewModel.sections.count 44 | } 45 | 46 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 47 | guard section < dashboardViewModel.sections.count else { 48 | return 0 49 | } 50 | 51 | return dashboardViewModel.sections[section].entries.count 52 | } 53 | 54 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 55 | guard let cell = tableView.dequeueReusableCell(withIdentifier: Constants.Identifier.Reuse.dashboardCell, for: indexPath) as? DashboardTableViewCell else { 56 | return UITableViewCell() 57 | } 58 | 59 | let group = dashboardViewModel.sections[indexPath.section] 60 | let entry = group.entries[indexPath.row] 61 | cell.setup(groupColor: group.color, classColor: entry.color, description: entry.description) 62 | 63 | return cell 64 | } 65 | 66 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 67 | return Constants.Layout.Dashboard.cellHeight 68 | } 69 | } 70 | 71 | // MARK: - UITableViewDelegate 72 | 73 | extension DashboardTableViewController: UITableViewDelegate { 74 | 75 | func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 76 | guard section < dashboardViewModel.sections.count, 77 | let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: Constants.Identifier.Reuse.dashboardHeader) as? DashboardTableViewHeaderView else { 78 | return nil 79 | } 80 | 81 | let section = dashboardViewModel.sections[section] 82 | headerView.setup(color: section.color, title: section.title) 83 | 84 | return headerView 85 | } 86 | 87 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 88 | return Constants.Layout.Dashboard.sectionHeaderHeight 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/iOS/UI/DashboardTableView/DashboardTableViewHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DashboardTableViewHeaderView.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Hans Seiffert on 21.10.17. 6 | // 7 | 8 | import UIKit 9 | 10 | class DashboardTableViewHeaderView: UITableViewHeaderFooterView { 11 | 12 | @IBOutlet private weak var indicatorView: UIView! 13 | @IBOutlet private weak var groupNameLabel: UILabel! 14 | 15 | class var nib: UINib { 16 | return UINib(nibName: String(describing: self), bundle: .resolvedBundle) 17 | } 18 | 19 | override func prepareForReuse() { 20 | super.prepareForReuse() 21 | indicatorView.backgroundColor = .clear 22 | groupNameLabel.text = nil 23 | } 24 | 25 | func setup(color: UIColor, title: String) { 26 | indicatorView.backgroundColor = color 27 | groupNameLabel.text = title 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/iOS/UI/Extensions+UIKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Hans Seiffert on 09.11.17. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal extension UIView { 12 | 13 | class var lt_nibInOwnBundle: UINib { 14 | return UINib(nibName: String(describing: self), bundle: .resolvedBundle) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/iOS/UI/HideOption.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HideOption.swift 3 | // LifetimeTracker-iOS 4 | // 5 | // Created by Thanh Duc Do on 29.08.18. 6 | // Copyright © 2018 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | enum HideOption { 10 | case untilMoreIssue 11 | case untilNewIssueType 12 | case always 13 | case none 14 | 15 | func shouldUIBeShown(oldModel: BarDashboardViewModel, newModel: BarDashboardViewModel) -> Bool { 16 | switch self { 17 | case .untilMoreIssue: 18 | if oldModel.leaksCount < newModel.leaksCount { 19 | return true 20 | } 21 | return false 22 | case .untilNewIssueType: 23 | var oldGroupModelTitleSet = Set() 24 | for oldGroupModel in oldModel.sections { 25 | oldGroupModelTitleSet.insert(oldGroupModel.groupName) 26 | } 27 | 28 | for newGroupModel in newModel.sections { 29 | if !oldGroupModelTitleSet.contains(newGroupModel.groupName) && newGroupModel.entries.count > newGroupModel.entries.capacity { 30 | return true 31 | } else if let oldGroupModel = oldModel.sections.first(where: { (groupModel: GroupModel) -> Bool in 32 | groupModel.groupName == newGroupModel.groupName 33 | }) { 34 | if oldGroupModel.groupCount<=oldGroupModel.groupMaxCount && newGroupModel.groupCount>newGroupModel.groupMaxCount { 35 | return true 36 | } 37 | } 38 | } 39 | return false 40 | default: 41 | return false 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/iOS/UI/LifetimeTrackerListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LifetimeTrackerListViewController.swift 3 | // LifetimeTracker-iOS 4 | // 5 | // Created by Hans Seiffert on 18.03.18. 6 | // Copyright © 2018 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol PopoverViewControllerDelegate: AnyObject { 12 | func dismissPopoverViewController() 13 | func changeHideOption(for hideOption: HideOption) 14 | } 15 | 16 | class LifetimeTrackerListViewController: UIViewController { 17 | 18 | weak var delegate: PopoverViewControllerDelegate? 19 | 20 | weak var tableViewController: DashboardTableViewController? 21 | 22 | var dashboardViewModel = BarDashboardViewModel() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | } 27 | 28 | @IBAction func closeButtonPressed(_ sender: Any) { 29 | delegate?.dismissPopoverViewController() 30 | } 31 | 32 | @IBAction func settingsButtonPressed(_ sender: Any) { 33 | SettingsManager.showSettingsActionSheet(on: self, completionHandler: { [weak self] (selectedOption: HideOption) in 34 | self?.delegate?.changeHideOption(for: selectedOption) 35 | }) 36 | } 37 | 38 | func update(dashboardViewModel: BarDashboardViewModel) { 39 | self.dashboardViewModel = dashboardViewModel 40 | 41 | title = "popover.dasboard.title".lt_localized 42 | 43 | tableViewController?.update(dashboardViewModel: dashboardViewModel) 44 | } 45 | 46 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 47 | if segue.identifier == Constants.Segue.embedDashboardTableView.identifier { 48 | tableViewController = segue.destination as? DashboardTableViewController 49 | tableViewController?.update(dashboardViewModel: dashboardViewModel) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tests/LifetimeTrackerTests/LifetimeTrackerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LifetimeTrackerTests.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Krzysztof Zablocki on 27/07/2017. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import LifetimeTracker 12 | 13 | private final class Foo {} 14 | 15 | private final class TrackableObject: NSObject, LifetimeTrackable { 16 | 17 | static var lifetimeConfiguration = LifetimeConfiguration(maxCount: 1, groupName: "TrackableObject") 18 | 19 | override init() { 20 | super.init() 21 | 22 | trackLifetime() 23 | } 24 | } 25 | 26 | class LifetimeTrackerTests: XCTestCase { 27 | 28 | override func setUp() { 29 | LifetimeTracker.setup { (trackedGroups: [String: LifetimeTracker.EntriesGroup]) in 30 | // Nothing to do here 31 | } 32 | } 33 | override func tearDown() { 34 | LifetimeTracker.instance = nil 35 | } 36 | 37 | func testDeallocTrackerFiresOnDealloc() { 38 | var fake: Foo? = Foo() 39 | var wasCalled = false 40 | 41 | onDealloc(of: fake as Any) { wasCalled = true } 42 | fake = nil 43 | 44 | XCTAssertTrue(wasCalled) 45 | } 46 | 47 | func testDeallocTrackerDoesntFire() { 48 | let fake: Foo? = Foo() 49 | var wasCalled = false 50 | 51 | onDealloc(of: fake as Any) { wasCalled = true } 52 | 53 | XCTAssertFalse(wasCalled) 54 | } 55 | 56 | func testEntryMaxCountDoesNotChangeAfterMultipleAllocations() { 57 | 58 | var currentEntryMaxCount: Int? { 59 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.entries[TrackableObject.lifetimeConfiguration.instanceName]?.maxCount 60 | } 61 | 62 | TrackableObject.lifetimeConfiguration = LifetimeConfiguration(maxCount: 1, groupName: "TrackableObject") 63 | 64 | // Create the initial object with a count of 1 65 | let _ = TrackableObject() 66 | 67 | // Test that the inital maxCount is used 68 | XCTAssertEqual(currentEntryMaxCount, 1, "Entry maxCount != 1 after the first initialization") 69 | 70 | // Test that a second allocation doesn't change the initial maxCount 71 | let _ = TrackableObject() 72 | XCTAssertEqual(currentEntryMaxCount, 1, "Entry maxCount != 1 after the first initialization") 73 | } 74 | 75 | func testConfigurationMaxCountIncrementationUpdatesEntryAndGroupMaxCount() { 76 | 77 | var currentEntryMaxCount: Int? { 78 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.entries[TrackableObject.lifetimeConfiguration.instanceName]?.maxCount 79 | } 80 | var currentGroupMaxCount: Int? { 81 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.maxCount 82 | } 83 | 84 | // Create the initial object with a count of 1 85 | TrackableObject.lifetimeConfiguration = LifetimeConfiguration(maxCount: 1, groupName: "TrackableObject") 86 | let _ = TrackableObject() 87 | 88 | // Test that the inital maxCount is used 89 | XCTAssertEqual(currentEntryMaxCount, 1, "Entry maxCount != 1 after the first initialization") 90 | XCTAssertEqual(currentGroupMaxCount, 1, "Group maxCount != 1 after the first initialization") 91 | 92 | // Test that a modification of the objects maxCount with a allocation or deallocation does change the entries and groups maxCount 93 | TrackableObject.lifetimeConfiguration.maxCount = 2 94 | let _ = TrackableObject() 95 | XCTAssertEqual(currentEntryMaxCount, 2, "Entry maxCount != 2 after the modification of maxCount") 96 | XCTAssertEqual(currentGroupMaxCount, 2, "Group maxCount != 2 after the modification of maxCount") 97 | } 98 | 99 | func testConfigurationMaxCountDecrementationUpdatesEntryAndGroupMaxCount() { 100 | 101 | var currentEntryMaxCount: Int? { 102 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.entries[TrackableObject.lifetimeConfiguration.instanceName]?.maxCount 103 | } 104 | var currentGroupMaxCount: Int? { 105 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.maxCount 106 | } 107 | 108 | // Create the initial object with a count of 3 109 | TrackableObject.lifetimeConfiguration = LifetimeConfiguration(maxCount: 3, groupName: "TrackableObject") 110 | let _ = TrackableObject() 111 | 112 | // Test that the inital maxCount is used 113 | XCTAssertEqual(currentEntryMaxCount, 3, "Entry maxCount != 3 after the first initialization") 114 | XCTAssertEqual(currentGroupMaxCount, 3, "Group maxCount != 3 after the first initialization") 115 | 116 | // Test that a modification of the objects maxCount after a new allocation does change the entries and groups maxCount 117 | TrackableObject.lifetimeConfiguration.maxCount = 2 118 | let _ = TrackableObject() 119 | XCTAssertEqual(currentEntryMaxCount, 2, "Entry maxCount != 2 after the modification of maxCount") 120 | XCTAssertEqual(currentGroupMaxCount, 2, "Group maxCount != 2 after the modification of maxCount") 121 | } 122 | 123 | func testConfigurationMaxCountIncrementationDoeNotChangeOverriddenGroupsMaxCount() { 124 | 125 | var currentEntryMaxCount: Int? { 126 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.entries[TrackableObject.lifetimeConfiguration.instanceName]?.maxCount 127 | } 128 | var currentGroupMaxCount: Int? { 129 | return LifetimeTracker.instance?.trackedGroups["TrackableObject"]?.maxCount 130 | } 131 | 132 | // Create the initial object with a count of 3 133 | TrackableObject.lifetimeConfiguration = LifetimeConfiguration(maxCount: 1, groupName: "TrackableObject", groupMaxCount: 2) 134 | let _ = TrackableObject() 135 | 136 | // Test that the inital maxCount is used 137 | XCTAssertEqual(currentEntryMaxCount, 1, "Entry maxCount != 1 after the first initialization") 138 | XCTAssertEqual(currentGroupMaxCount, 2, "Overriden group maxCount != 2 after the first initialization") 139 | 140 | // Test that a modification of the objects maxCount after a new allocation doesn't change the overriden group maxCount 141 | TrackableObject.lifetimeConfiguration.maxCount = 2 142 | let _ = TrackableObject() 143 | XCTAssertEqual(currentEntryMaxCount, 2, "Entry maxCount != 2 after the modification of maxCount") 144 | XCTAssertEqual(currentGroupMaxCount, 2, "Overriden group maxCount != 2 after the modification of maxCount") 145 | } 146 | 147 | func testLeakClosure() { 148 | var hasLeaked = false 149 | LifetimeTracker.instance?.onLeakDetected = { entry, group in 150 | hasLeaked = true 151 | } 152 | 153 | TrackableObject.lifetimeConfiguration = LifetimeConfiguration(maxCount: 1) 154 | var trackables = [TrackableObject]() 155 | trackables.append(TrackableObject()) 156 | XCTAssert(!hasLeaked) 157 | trackables.append(TrackableObject()) 158 | XCTAssert(hasLeaked) 159 | } 160 | 161 | static var allTests = [ 162 | ("testDeallocTrackerFiresOnDealloc", testDeallocTrackerFiresOnDealloc), 163 | ("testDeallocTrackerDoesntFire", testDeallocTrackerDoesntFire), 164 | ("testEntryMaxCountDoesNotChangeAfterMultipleAllocations", testEntryMaxCountDoesNotChangeAfterMultipleAllocations), 165 | ("testConfigurationMaxCountIncrementationUpdatesEntryAndGroupMaxCount", testConfigurationMaxCountIncrementationUpdatesEntryAndGroupMaxCount), 166 | ("testConfigurationMaxCountDecrementationUpdatesEntryAndGroupMaxCount", testConfigurationMaxCountDecrementationUpdatesEntryAndGroupMaxCount), 167 | ("testConfigurationMaxCountIncrementationDoeNotChangeOverriddenGroupsMaxCount", testConfigurationMaxCountIncrementationDoeNotChangeOverriddenGroupsMaxCount), 168 | ("testLeakClosure", testLeakClosure) 169 | ] 170 | } 171 | -------------------------------------------------------------------------------- /Tests/LifetimeTrackerTests/VisibilityTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VisibilityTests.swift 3 | // LifetimeTracker 4 | // 5 | // Created by Jim Roepcke on 2017-10-24. 6 | // Copyright © 2017 LifetimeTracker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import LifetimeTracker 12 | 13 | private typealias Visibility = LifetimeTrackerDashboardIntegration.Visibility 14 | 15 | class VisibilityTests: XCTestCase { 16 | 17 | func testHidesWindowWhenBehaviorIsAlwaysHiddenAndThereAreNoIssuesToDisplay() { 18 | let behavior = Visibility.alwaysHidden 19 | let hasIssuesToDisplay = false 20 | XCTAssertTrue(behavior.windowIsHidden(hasIssuesToDisplay: hasIssuesToDisplay)) 21 | } 22 | 23 | func testHidesWindowWhenBehaviorIsAlwaysHiddenAndThereAreIssuesToDisplay() { 24 | let behavior = Visibility.alwaysHidden 25 | let hasIssuesToDisplay = true 26 | XCTAssertTrue(behavior.windowIsHidden(hasIssuesToDisplay: hasIssuesToDisplay)) 27 | } 28 | 29 | func testDoesNotHideWindowWhenBehaviorIsAlwaysVisibleAndThereAreNoIssuesToDisplay() { 30 | let behavior = Visibility.alwaysVisible 31 | let hasIssuesToDisplay = false 32 | XCTAssertFalse(behavior.windowIsHidden(hasIssuesToDisplay: hasIssuesToDisplay)) 33 | } 34 | 35 | func testDoesNotHideWindowWhenBehaviorIsAlwaysVisibleAndThereAreIssuesToDisplay() { 36 | let behavior = Visibility.alwaysVisible 37 | let hasIssuesToDisplay = true 38 | XCTAssertFalse(behavior.windowIsHidden(hasIssuesToDisplay: hasIssuesToDisplay)) 39 | } 40 | 41 | func testHidesWindowWhenBehaviorIsVisibleWithIssuesDetectedAndThereAreNoIssuesToDisplay() { 42 | let behavior = Visibility.visibleWithIssuesDetected 43 | let hasIssuesToDisplay = false 44 | XCTAssertTrue(behavior.windowIsHidden(hasIssuesToDisplay: hasIssuesToDisplay)) 45 | } 46 | 47 | func testDoesNotHideWindowWhenBehaviorIsVisibleWithIssuesDetectedAndThereAreIssuesToDisplay() { 48 | let behavior = Visibility.visibleWithIssuesDetected 49 | let hasIssuesToDisplay = true 50 | XCTAssertFalse(behavior.windowIsHidden(hasIssuesToDisplay: hasIssuesToDisplay)) 51 | } 52 | 53 | static var allTests = [ 54 | ("testHidesWindowWhenBehaviorIsAlwaysHiddenAndThereAreNoIssuesToDisplay", testHidesWindowWhenBehaviorIsAlwaysHiddenAndThereAreNoIssuesToDisplay), 55 | ("testHidesWindowWhenBehaviorIsAlwaysHiddenAndThereAreIssuesToDisplay", testHidesWindowWhenBehaviorIsAlwaysHiddenAndThereAreIssuesToDisplay), 56 | ("testDoesNotHideWindowWhenBehaviorIsAlwaysVisibleAndThereAreNoIssuesToDisplay", testDoesNotHideWindowWhenBehaviorIsAlwaysVisibleAndThereAreNoIssuesToDisplay), 57 | ("testDoesNotHideWindowWhenBehaviorIsAlwaysVisibleAndThereAreIssuesToDisplay", testDoesNotHideWindowWhenBehaviorIsAlwaysVisibleAndThereAreIssuesToDisplay), 58 | ("testHidesWindowWhenBehaviorIsVisibleWithIssuesDetectedAndThereAreNoIssuesToDisplay", testHidesWindowWhenBehaviorIsVisibleWithIssuesDetectedAndThereAreNoIssuesToDisplay), 59 | ("testDoesNotHideWindowWhenBehaviorIsVisibleWithIssuesDetectedAndThereAreIssuesToDisplay", testDoesNotHideWindowWhenBehaviorIsVisibleWithIssuesDetectedAndThereAreIssuesToDisplay), 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LifetimeTrackerTests 3 | 4 | XCTMain([ 5 | testCase(LifetimeTrackerTests.allTests), 6 | testCase(VisibilityTests.allTests), 7 | ]) 8 | --------------------------------------------------------------------------------