├── README.md ├── ShareExtension ├── Base.lproj │ └── MainInterface.storyboard ├── DictionaryCell.swift ├── ImageCell.swift ├── Info.plist ├── ShareViewController.swift ├── ShareViewModel.swift ├── String+Utility.swift ├── TextCell.swift └── UITableViewModel.swift ├── ShareInspector.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── ShareInspector ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ ├── Icon-App-83.5x83.5@2x.png │ │ └── Icon-App-iTunes.png │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── ViewController.swift └── screenshots ├── Device-PhotosApp-LivePhoto.png ├── Device-Safari-WebPage.png ├── Screenshot-PhotosApp-LivePhoto.png └── Screenshot-Safari-WebPage.png /README.md: -------------------------------------------------------------------------------- 1 | # Share Inspector 2 | 3 | An iOS app that provides a [share extension](https://developer.apple.com/design/human-interface-guidelines/ios/extensions/sharing-and-actions/) for inspecting the data the host app provides via the share sheet. 4 | 5 | Made by Cocologics, makers of [ProCamera](https://www.procamera-app.com). 6 | 7 | ## Usage 8 | 9 | 1. Download or clone the repository. 10 | 2. Open the project in Xcode and run it on the simulator or an iOS device. 11 | 3. Open the share sheet in any app and pick Share Inspector in the share sheet. 12 | 13 | ## Requirements 14 | 15 | iOS 12.0 or later.
16 | No dependencies. 17 | 18 | ## Screenshots 19 | 20 | ### Receiving a Live Photo shared from Photos 21 | 22 | Share Inspector receiving a Live Photo shared from Photos 23 | 24 | ### Receiving a web page shared from Safari 25 | 26 | Share Inspector receiving a web page shared from Safari 27 | -------------------------------------------------------------------------------- /ShareExtension/Base.lproj/MainInterface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 128 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /ShareExtension/DictionaryCell.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A UITableViewCell that displays a label and the contents of a dictionary. 4 | /// The dictionary keys must be strings. 5 | final class DictionaryCell: UITableViewCell, ReusableCell, ResizingCell { 6 | static let reuseIdentifier = "DictionaryCell" 7 | static let shouldRegisterCellClassWithTableView = false 8 | 9 | @IBOutlet var label: UILabel! 10 | /// The stack view in which the elements of the dictionary are displayed 11 | @IBOutlet var elementsStack: UIStackView! 12 | @IBOutlet var expandButton: UIButton! 13 | 14 | var viewModel: LabeledValue<[String: Any]?>? { 15 | didSet { updateUI() } 16 | } 17 | 18 | var isExpanded: Bool = false { 19 | didSet { 20 | updateUI() 21 | cellDidResize?() 22 | } 23 | } 24 | 25 | var cellDidResize: (() -> ())? 26 | 27 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 28 | super.init(style: style, reuseIdentifier: reuseIdentifier) 29 | updateUI() 30 | } 31 | 32 | required init?(coder: NSCoder) { 33 | super.init(coder: coder) 34 | } 35 | 36 | override func awakeFromNib() { 37 | super.awakeFromNib() 38 | label.preferredMaxLayoutWidth = CGFloat.greatestFiniteMagnitude 39 | expandButton.titleLabel?.adjustsFontForContentSizeCategory = true 40 | updateUI() 41 | } 42 | 43 | override func prepareForReuse() { 44 | super.prepareForReuse() 45 | cellDidResize = nil 46 | } 47 | 48 | @IBAction func toggleExpandCollapse() { 49 | isExpanded.toggle() 50 | } 51 | 52 | private func updateUI() { 53 | // Remove all existing views in elementsStack 54 | for subview in elementsStack.arrangedSubviews { 55 | subview.removeFromSuperview() 56 | } 57 | 58 | // Rebuild elementsStack from current state 59 | if let viewModel = viewModel { 60 | label.text = viewModel.label 61 | expandButton.setTitle(isExpanded ? "Collapse" : "Expand", for: .normal) 62 | 63 | switch (viewModel.value, isExpanded) { 64 | case (let dict?, true): 65 | expandButton.isHidden = false 66 | let subStacks = dict 67 | .sorted(by: { $0.key < $1.key }) 68 | .map(DictionaryCell.makeSubStack(element:)) 69 | subStacks.forEach(elementsStack.addArrangedSubview) 70 | case (let dict?, false): 71 | expandButton.isHidden = false 72 | let infoLabel = UILabel() 73 | infoLabel.font = .preferredFont(forTextStyle: .body) 74 | infoLabel.text = "\(dict.count) key/value \(dict.count == 1 ? "pair" : "pairs")" 75 | elementsStack.addArrangedSubview(infoLabel) 76 | case (nil, _): 77 | expandButton.isHidden = true 78 | let nilLabel = UILabel() 79 | nilLabel.font = .preferredFont(forTextStyle: .body) 80 | nilLabel.text = "(nil)" 81 | elementsStack.addArrangedSubview(nilLabel) 82 | } 83 | } else { 84 | label.text = nil 85 | expandButton.isHidden = true 86 | } 87 | } 88 | 89 | private class func makeSubStack(element: (key: String, value: Any)) -> UIStackView { 90 | let (key, value) = element 91 | let keyLabel = UILabel() 92 | keyLabel.font = .preferredFont(forTextStyle: .body) 93 | keyLabel.numberOfLines = 0 94 | keyLabel.text = key 95 | 96 | let valueLabel = UILabel() 97 | valueLabel.font = .preferredFont(forTextStyle: .body) 98 | valueLabel.numberOfLines = 0 99 | valueLabel.text = String(describing: value) 100 | 101 | let stack = UIStackView(arrangedSubviews: [ 102 | keyLabel, 103 | valueLabel 104 | ]) 105 | stack.axis = .vertical 106 | stack.spacing = 4 107 | return stack 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ShareExtension/ImageCell.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A UITableViewCell that displays a label and an image. 4 | /// The image is loaded dynamically by the cell. 5 | final class ImageCell: UITableViewCell, ReusableCell { 6 | static let reuseIdentifier = "ImageCell" 7 | static let shouldRegisterCellClassWithTableView = false 8 | 9 | static let smallImageSize: CGFloat = 50 10 | static let largeImageSize: CGFloat = 120 11 | 12 | @IBOutlet var label: UILabel! 13 | /// Displays meta information such as image size or error messages if loading fails. 14 | @IBOutlet var infoLabel: UILabel! 15 | @IBOutlet var thumbnailView: UIImageView! 16 | @IBOutlet var thumbnailViewHeightConstraint: NSLayoutConstraint! 17 | @IBOutlet var thumbnailViewWidthConstraint: NSLayoutConstraint! 18 | 19 | var viewModel: LabeledValue? { 20 | didSet { 21 | updateUI() 22 | viewModel?.value(CGSize(width: ImageCell.largeImageSize, height: 2 * ImageCell.largeImageSize)) { result in 23 | // TODO: Handle cell reuse. Do nothing if the cell has been reused in the meantime. 24 | DispatchQueue.main.async { 25 | self.loadedImage = result 26 | } 27 | } 28 | } 29 | } 30 | 31 | private var loadedImage: Result? { 32 | didSet { updateUI() } 33 | } 34 | 35 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 36 | super.init(style: style, reuseIdentifier: reuseIdentifier) 37 | updateUI() 38 | } 39 | 40 | required init?(coder: NSCoder) { 41 | super.init(coder: coder) 42 | } 43 | 44 | override func awakeFromNib() { 45 | super.awakeFromNib() 46 | label.preferredMaxLayoutWidth = CGFloat.greatestFiniteMagnitude 47 | infoLabel.preferredMaxLayoutWidth = CGFloat.greatestFiniteMagnitude 48 | updateUI() 49 | } 50 | 51 | private func updateUI() { 52 | if let viewModel = viewModel { 53 | label.text = viewModel.label 54 | switch loadedImage { 55 | case nil: 56 | thumbnailView.image = nil 57 | thumbnailViewHeightConstraint.constant = ImageCell.smallImageSize 58 | thumbnailViewWidthConstraint.constant = ImageCell.smallImageSize 59 | infoLabel.text = nil 60 | case .success(let image)?: 61 | let aspectRatio = image.size.width / image.size.height 62 | let isValidAspectRatio = aspectRatio.isFinite && aspectRatio > 0 63 | thumbnailView.image = image 64 | thumbnailViewHeightConstraint.constant = ImageCell.largeImageSize 65 | thumbnailViewWidthConstraint.constant = isValidAspectRatio ? aspectRatio * ImageCell.largeImageSize : ImageCell.largeImageSize 66 | infoLabel.text = "\(image.size.width) × \(image.size.height) pt" 67 | case .failure(let error): 68 | thumbnailView.image = nil 69 | if let error = error as? ShareInspectorError, error.code == .noPreviewImageProvided { 70 | thumbnailViewHeightConstraint.constant = ImageCell.smallImageSize 71 | thumbnailViewWidthConstraint.constant = ImageCell.smallImageSize 72 | infoLabel.text = "(none)" 73 | } else { 74 | thumbnailViewHeightConstraint.constant = ImageCell.largeImageSize 75 | thumbnailViewWidthConstraint.constant = ImageCell.largeImageSize 76 | infoLabel.text = "Loading error: \(error.localizedDescription)" 77 | } 78 | } 79 | } else { 80 | label.text = nil 81 | infoLabel.text = nil 82 | thumbnailView.image = nil 83 | thumbnailViewHeightConstraint.constant = ImageCell.smallImageSize 84 | thumbnailViewWidthConstraint.constant = ImageCell.smallImageSize 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ShareExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Share Inspector 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | NSExtensionActivationRule 28 | TRUEPREDICATE 29 | 30 | NSExtensionMainStoryboard 31 | MainInterface 32 | NSExtensionPointIdentifier 33 | com.apple.share-services 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /ShareExtension/ShareViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ShareViewController: UIViewController { 4 | var viewModel = ShareViewModel(extensionContext: nil) { 5 | didSet { 6 | guard isViewLoaded else { return } 7 | tableView?.reloadData() 8 | } 9 | } 10 | 11 | @IBOutlet var tableView: UITableView? 12 | private var reuseIdentifiers: Set = [] 13 | 14 | override func loadView() { 15 | super.loadView() 16 | tableView!.rowHeight = UITableView.automaticDimension 17 | viewModel = ShareViewModel(extensionContext: extensionContext) 18 | } 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | // Set our tint color on the parent navigation controller. 23 | // We do this from here because this is our entry point for the Share Extension. 24 | navigationController?.view.tintColor = .systemGreen 25 | } 26 | 27 | @IBAction func done(_ sender: UIBarButtonItem) { 28 | extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) 29 | } 30 | } 31 | 32 | extension ShareViewController: UITableViewDataSource { 33 | func numberOfSections(in tableView: UITableView) -> Int { 34 | return viewModel.sections.count 35 | } 36 | 37 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 38 | return viewModel.sections[section].cells.count 39 | } 40 | 41 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 42 | return viewModel.sections[section].sectionTitle 43 | } 44 | 45 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 46 | let section = viewModel.sections[indexPath.section] 47 | let descriptor = section.cells[indexPath.row] 48 | 49 | // Register cell types lazily 50 | if descriptor.shouldRegisterCellClassWithTableView && !reuseIdentifiers.contains(descriptor.reuseIdentifier) { 51 | tableView.register(descriptor.cellClass, forCellReuseIdentifier: descriptor.reuseIdentifier) 52 | reuseIdentifiers.insert(descriptor.reuseIdentifier) 53 | } 54 | 55 | let cell = tableView.dequeueReusableCell(withIdentifier: descriptor.reuseIdentifier, for: indexPath) 56 | if let resizingCell = cell as? ResizingCell { 57 | resizingCell.cellDidResize = { [weak tableView] in 58 | guard let tableView = tableView else { return } 59 | tableView.beginUpdates() 60 | tableView.endUpdates() 61 | } 62 | } 63 | descriptor.configure(cell) 64 | return cell 65 | } 66 | } 67 | 68 | extension ShareViewController: UITableViewDelegate { 69 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 70 | // Hack: Reload the cell in order to resize it (to fix autolayout issues) 71 | tableView.beginUpdates() 72 | tableView.reloadRows(at: [indexPath], with: .automatic) 73 | tableView.endUpdates() 74 | tableView.deselectRow(at: indexPath, animated: true) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ShareExtension/ShareViewModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | typealias LoadImageHandler = (_ preferredSize: CGSize, _ callback: @escaping (Result) -> ()) -> () 5 | 6 | struct LabeledValue { 7 | var label: String 8 | var value: Value 9 | } 10 | 11 | enum Text { 12 | case plain(String) 13 | case rich(NSAttributedString) 14 | } 15 | 16 | extension LabeledValue where Value == Text { 17 | var cellDescriptor: CellDescriptor { 18 | return CellDescriptor { (cell: TextCell) in 19 | cell.viewModel = self 20 | } 21 | } 22 | } 23 | 24 | extension LabeledValue where Value == [String: Any]? { 25 | var cellDescriptor: CellDescriptor { 26 | return CellDescriptor { (cell: DictionaryCell) in 27 | cell.viewModel = self 28 | } 29 | } 30 | } 31 | 32 | extension LabeledValue where Value == LoadImageHandler { 33 | var cellDescriptor: CellDescriptor { 34 | return CellDescriptor { (cell: ImageCell) in 35 | cell.viewModel = self 36 | } 37 | } 38 | } 39 | 40 | /// View model for ShareViewController 41 | struct ShareViewModel { 42 | var sections: [Section] 43 | 44 | init(extensionContext: NSExtensionContext?) { 45 | self.sections = ShareViewModel.makeSections(extensionContext: extensionContext) 46 | } 47 | 48 | private static func makeSections(extensionContext: NSExtensionContext?) -> [Section] { 49 | var sections: [Section] = [] 50 | 51 | // Overview section 52 | guard let extensionContext = extensionContext else { 53 | sections.append(Notice(label: "Error", message: "Share extension received no NSExtensionContext.")) 54 | return sections 55 | } 56 | sections.append(Overview(sharedItemsCount: extensionContext.inputItems.count)) 57 | 58 | guard let sharedItems = extensionContext.inputItems as? [NSExtensionItem] else { 59 | sections.append(Notice(label: "Error", message: "Error: Elements in extensionContext.inputItems are not of type NSExtensionItem.")) 60 | return sections 61 | } 62 | 63 | // Sections for the shared items 64 | // For each shared item, we create: 65 | // - An overview section with the data provided by NSExtensionItem. 66 | // - One attachment section per attachment (NSItemProvider) of the shared item. 67 | for (itemNumber, sharedItem) in zip(1..., sharedItems) { 68 | let attachments = sharedItem.attachments ?? [] 69 | 70 | // Shared item overview 71 | sections.append(SharedItemOverview( 72 | itemNumber: itemNumber, 73 | attributedTitle: sharedItem.attributedTitle, 74 | attributedContentText: sharedItem.attributedContentText, 75 | attachmentCount: attachments.count, 76 | userInfo: sharedItem.userInfo as? [String: Any])) 77 | 78 | // Attachments 79 | for (attachmentNumber, itemProvider) in zip(1..., attachments) { 80 | sections.append(Attachment( 81 | itemNumber: itemNumber, 82 | attachmentNumber: attachmentNumber, 83 | attachmentCount: attachments.count, 84 | itemProvider: itemProvider)) 85 | } 86 | } 87 | 88 | return sections 89 | } 90 | } 91 | 92 | extension ShareViewModel { 93 | /// A section for displaying a piece of text (e.g. an error message) 94 | struct Notice: Section { 95 | var label: String 96 | var message: String 97 | 98 | var sectionTitle: String? { return nil } 99 | var cells: [CellDescriptor] { 100 | return [ 101 | LabeledValue(label: label, value: Text.plain(message)).cellDescriptor 102 | ] 103 | } 104 | } 105 | 106 | struct Overview: Section { 107 | var sharedItemsCount: Int 108 | 109 | var sectionTitle: String? { return nil } 110 | var cells: [CellDescriptor] { 111 | return [ 112 | LabeledValue( 113 | label: "Number of shared items", 114 | value: Text.plain("\(sharedItemsCount)")).cellDescriptor, 115 | ] 116 | } 117 | } 118 | 119 | struct SharedItemOverview: Section { 120 | var itemNumber: Int 121 | var attributedTitle: NSAttributedString? 122 | var attributedContentText: NSAttributedString? 123 | var attachmentCount: Int 124 | var userInfo: [String: Any]? 125 | 126 | var sectionTitle: String? { 127 | return "Item \(itemNumber) (NSExtensionItem)" 128 | } 129 | 130 | var cells: [CellDescriptor] { 131 | return [ 132 | LabeledValue( 133 | label: "attributedTitle", 134 | value: attributedTitle.map(Text.rich) ?? Text.plain("(nil)")).cellDescriptor, 135 | LabeledValue( 136 | label: "attributedContentText", 137 | value: attributedContentText.map(Text.rich) ?? Text.plain("(nil)")).cellDescriptor, 138 | LabeledValue( 139 | label: "Number of attachments", 140 | value: Text.plain("\(attachmentCount)")).cellDescriptor, 141 | LabeledValue( 142 | label: "userInfo", 143 | value: userInfo).cellDescriptor, 144 | ] 145 | } 146 | } 147 | 148 | struct Attachment: Section { 149 | var itemNumber: Int 150 | var attachmentNumber: Int 151 | var attachmentCount: Int 152 | var registeredTypeIdentifiers: [String] 153 | var suggestedName: String? 154 | var loadPreviewImage: LoadImageHandler 155 | 156 | init(itemNumber: Int, attachmentNumber: Int, attachmentCount: Int, itemProvider: NSItemProvider) { 157 | self.itemNumber = itemNumber 158 | self.attachmentNumber = attachmentNumber 159 | self.attachmentCount = attachmentCount 160 | self.registeredTypeIdentifiers = itemProvider.registeredTypeIdentifiers 161 | self.suggestedName = itemProvider.suggestedName 162 | self.loadPreviewImage = Attachment.makeLoadPreviewImageHandler(itemProvider: itemProvider) 163 | } 164 | 165 | var sectionTitle: String? { 166 | return "Item \(itemNumber) · Attachment \(attachmentNumber) of \(attachmentCount) (NSItemProvider)" 167 | } 168 | 169 | var cells: [CellDescriptor] { 170 | let bullet = registeredTypeIdentifiers.count == 1 ? "" : "• " 171 | let typeIdentifiersList = registeredTypeIdentifiers 172 | .map { line in "\(bullet)\(line)" } 173 | .joined(separator: "\n") 174 | return [ 175 | LabeledValue( 176 | label: "Preview Image", 177 | value: loadPreviewImage).cellDescriptor, 178 | LabeledValue( 179 | label: "registeredTypeIdentifiers", 180 | value: Text.plain(typeIdentifiersList)).cellDescriptor, 181 | LabeledValue( 182 | label: "suggestedName", 183 | value: Text.plain(suggestedName ?? "(nil)")).cellDescriptor, 184 | ] 185 | } 186 | 187 | private static func makeLoadPreviewImageHandler(itemProvider: NSItemProvider) -> LoadImageHandler { 188 | let loadPreviewImage: LoadImageHandler = { preferredSize, callback in 189 | let options: [AnyHashable: Any] = [ 190 | NSItemProviderPreferredImageSizeKey: NSValue(cgSize: preferredSize) 191 | ] 192 | let completionHandler: (NSSecureCoding?, Error?) -> () = { result, error in 193 | switch (result, error) { 194 | case (let image as UIImage, _): 195 | callback(.success(image)) 196 | case (_?, _): 197 | callback(.failure(error ?? ShareInspectorError.unableToCastToUIImage(actualType: "\(type(of: result))"))) 198 | case (nil, let error?): 199 | callback(.failure(error)) 200 | case (nil, nil): 201 | callback(.failure(ShareInspectorError.noPreviewImageProvided)) 202 | } 203 | } 204 | itemProvider.loadPreviewImage(options: options, completionHandler: completionHandler) 205 | } 206 | return loadPreviewImage 207 | } 208 | } 209 | } 210 | 211 | struct ShareInspectorError: Error { 212 | enum Code { 213 | case unableToCastToUIImage 214 | case noPreviewImageProvided 215 | } 216 | 217 | var code: Code 218 | var message: String 219 | 220 | static func unableToCastToUIImage(actualType: String) -> ShareInspectorError { 221 | return ShareInspectorError(code: .unableToCastToUIImage, message: "Expected preview image to be UIImage, host app provided \(actualType)") 222 | } 223 | static let noPreviewImageProvided = ShareInspectorError(code: .noPreviewImageProvided, message: "Host app provided no preview image") 224 | } 225 | -------------------------------------------------------------------------------- /ShareExtension/String+Utility.swift: -------------------------------------------------------------------------------- 1 | extension StringProtocol { 2 | var lineCount: Int { 3 | guard !isEmpty else { 4 | return 0 5 | } 6 | return reduce(1) { lineCount, character in 7 | lineCount + (character.isNewline ? 1 : 0) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareExtension/TextCell.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A UITableViewCell that displays a label and a string value. 4 | /// The value can be plain (`String`) or rich text (`NSAttributedString)`. 5 | final class TextCell: UITableViewCell, ReusableCell { 6 | static let reuseIdentifier = "TextCell" 7 | static let shouldRegisterCellClassWithTableView = false 8 | 9 | @IBOutlet var contentStack: UIStackView! 10 | @IBOutlet var label: UILabel! 11 | @IBOutlet var valueLabel: UILabel! 12 | 13 | var viewModel: LabeledValue? { 14 | didSet { updateUI() } 15 | } 16 | 17 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 18 | super.init(style: style, reuseIdentifier: reuseIdentifier) 19 | updateUI() 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | super.init(coder: coder) 24 | } 25 | 26 | override func awakeFromNib() { 27 | super.awakeFromNib() 28 | label.preferredMaxLayoutWidth = CGFloat.greatestFiniteMagnitude 29 | valueLabel.preferredMaxLayoutWidth = CGFloat.greatestFiniteMagnitude 30 | updateUI() 31 | } 32 | 33 | override func layoutSubviews() { 34 | super.layoutSubviews() 35 | 36 | let layoutAxis = preferredLayoutAxis 37 | switch layoutAxis { 38 | case .horizontal where contentStack.axis != .horizontal: 39 | contentStack.axis = .horizontal 40 | contentStack.distribution = .fill 41 | contentStack.alignment = .firstBaseline 42 | valueLabel.textAlignment = .right 43 | case .vertical where contentStack.axis != .vertical: 44 | contentStack.axis = .vertical 45 | contentStack.distribution = .fill 46 | contentStack.alignment = .fill 47 | valueLabel.textAlignment = .left 48 | default: 49 | break 50 | } 51 | } 52 | 53 | private func updateUI() { 54 | if let viewModel = viewModel { 55 | label.text = viewModel.label 56 | switch viewModel.value { 57 | case .plain(let text): valueLabel.text = text 58 | case .rich(let attributedText): valueLabel.attributedText = attributedText 59 | } 60 | } else { 61 | label.text = nil 62 | valueLabel.text = nil 63 | } 64 | } 65 | 66 | private var preferredLayoutAxis: NSLayoutConstraint.Axis { 67 | let labelWidth = label.intrinsicContentSize.width 68 | let valueWidth = valueLabel.intrinsicContentSize.width 69 | let hasValidIntrinsicWidth = labelWidth != UIView.noIntrinsicMetric && valueWidth != UIView.noIntrinsicMetric 70 | guard hasValidIntrinsicWidth else { 71 | return .vertical 72 | } 73 | 74 | let valueHasLineBreaks = (valueLabel.text ?? "").lineCount > 1 75 | guard !valueHasLineBreaks else { 76 | return .vertical 77 | } 78 | 79 | let requiredWidthIfHorizontal = labelWidth + contentStack.spacing + valueWidth 80 | let hasEnoughHorizontalSpace = contentStack.bounds.width >= requiredWidthIfHorizontal 81 | return hasEnoughHorizontalSpace ? .horizontal : .vertical 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ShareExtension/UITableViewModel.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A section in a table view. 4 | protocol Section { 5 | var sectionTitle: String? { get } 6 | var cells: [CellDescriptor] { get } 7 | } 8 | 9 | /// A recipe for creating a specific table view cell. 10 | /// 11 | /// Inspired by: [objc.io, Generic Table View Controllers](https://talk.objc.io/episodes/S01E26-generic-table-view-controllers-part-2) 12 | struct CellDescriptor { 13 | let cellClass: UITableViewCell.Type 14 | let reuseIdentifier: String 15 | let shouldRegisterCellClassWithTableView: Bool 16 | // Type-erased in order to use multiple concrete CellDescriptor values 17 | // in a single table view. 18 | let configure: (UITableViewCell) -> () 19 | 20 | init(configure: @escaping (Cell) -> ()) { 21 | self.cellClass = Cell.self 22 | self.reuseIdentifier = Cell.self.reuseIdentifier 23 | self.shouldRegisterCellClassWithTableView = Cell.self.shouldRegisterCellClassWithTableView 24 | self.configure = { cell in 25 | configure(cell as! Cell) 26 | } 27 | } 28 | } 29 | 30 | protocol ReusableCell { 31 | static var reuseIdentifier: String { get } 32 | /// Should be false if the table view doesn't have to register the cell class. 33 | /// For example, this is the case for cells that are created as prototype cells 34 | /// of the table view in a storyboard. 35 | /// Should be true for cells that are created in code. 36 | static var shouldRegisterCellClassWithTableView: Bool { get } 37 | } 38 | 39 | protocol ResizingCell: UITableViewCell { 40 | var cellDidResize: (() -> ())? { get set } 41 | } 42 | -------------------------------------------------------------------------------- /ShareInspector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5D0A79E122E9F02D007C84B1 /* DictionaryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0A79E022E9F02D007C84B1 /* DictionaryCell.swift */; }; 11 | 5D0A79E322EA28A9007C84B1 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0A79E222EA28A9007C84B1 /* ImageCell.swift */; }; 12 | 5D0A79E522EAE522007C84B1 /* String+Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0A79E422EAE522007C84B1 /* String+Utility.swift */; }; 13 | 5D74E33B22E8571100EFF3AA /* ShareViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D74E33A22E8571100EFF3AA /* ShareViewModel.swift */; }; 14 | 5D74E34222E8D60300EFF3AA /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D74E34122E8D60300EFF3AA /* TextCell.swift */; }; 15 | 5D74E34422E8D95300EFF3AA /* UITableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D74E34322E8D95300EFF3AA /* UITableViewModel.swift */; }; 16 | 5DDBB6C722E5C89F00BEA421 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDBB6C622E5C89F00BEA421 /* AppDelegate.swift */; }; 17 | 5DDBB6C922E5C89F00BEA421 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDBB6C822E5C89F00BEA421 /* ViewController.swift */; }; 18 | 5DDBB6CC22E5C89F00BEA421 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5DDBB6CA22E5C89F00BEA421 /* Main.storyboard */; }; 19 | 5DDBB6CE22E5C8A000BEA421 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5DDBB6CD22E5C8A000BEA421 /* Assets.xcassets */; }; 20 | 5DDBB6D122E5C8A000BEA421 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5DDBB6CF22E5C8A000BEA421 /* LaunchScreen.storyboard */; }; 21 | 5DDBB6F222E5CA6400BEA421 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDBB6F122E5CA6400BEA421 /* ShareViewController.swift */; }; 22 | 5DDBB6F522E5CA6400BEA421 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5DDBB6F322E5CA6400BEA421 /* MainInterface.storyboard */; }; 23 | 5DDBB6F922E5CA6400BEA421 /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 5DDBB6EF22E5CA6400BEA421 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 5DDBB6F722E5CA6400BEA421 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 5DDBB6BB22E5C89F00BEA421 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 5DDBB6EE22E5CA6400BEA421; 32 | remoteInfo = ShareExtension; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXCopyFilesBuildPhase section */ 37 | 5DDBB6FD22E5CA6400BEA421 /* Embed App Extensions */ = { 38 | isa = PBXCopyFilesBuildPhase; 39 | buildActionMask = 2147483647; 40 | dstPath = ""; 41 | dstSubfolderSpec = 13; 42 | files = ( 43 | 5DDBB6F922E5CA6400BEA421 /* ShareExtension.appex in Embed App Extensions */, 44 | ); 45 | name = "Embed App Extensions"; 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXCopyFilesBuildPhase section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 5D0A79E022E9F02D007C84B1 /* DictionaryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DictionaryCell.swift; sourceTree = ""; }; 52 | 5D0A79E222EA28A9007C84B1 /* ImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCell.swift; sourceTree = ""; }; 53 | 5D0A79E422EAE522007C84B1 /* String+Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Utility.swift"; sourceTree = ""; }; 54 | 5D74E33A22E8571100EFF3AA /* ShareViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewModel.swift; sourceTree = ""; }; 55 | 5D74E33C22E85ADD00EFF3AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 56 | 5D74E34122E8D60300EFF3AA /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = ""; }; 57 | 5D74E34322E8D95300EFF3AA /* UITableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewModel.swift; sourceTree = ""; }; 58 | 5DDBB6C322E5C89F00BEA421 /* ShareInspector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShareInspector.app; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 5DDBB6C622E5C89F00BEA421 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 60 | 5DDBB6C822E5C89F00BEA421 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 61 | 5DDBB6CB22E5C89F00BEA421 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 62 | 5DDBB6CD22E5C8A000BEA421 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 63 | 5DDBB6D022E5C8A000BEA421 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 64 | 5DDBB6D222E5C8A000BEA421 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 5DDBB6EF22E5CA6400BEA421 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 5DDBB6F122E5CA6400BEA421 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 67 | 5DDBB6F422E5CA6400BEA421 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; 68 | 5DDBB6F622E5CA6400BEA421 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 5DDBB6C022E5C89F00BEA421 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 5DDBB6EC22E5CA6400BEA421 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 5DDBB6BA22E5C89F00BEA421 = { 90 | isa = PBXGroup; 91 | children = ( 92 | 5D74E33C22E85ADD00EFF3AA /* README.md */, 93 | 5DDBB6C522E5C89F00BEA421 /* ShareInspector */, 94 | 5DDBB6F022E5CA6400BEA421 /* ShareExtension */, 95 | 5DDBB6C422E5C89F00BEA421 /* Products */, 96 | ); 97 | sourceTree = ""; 98 | }; 99 | 5DDBB6C422E5C89F00BEA421 /* Products */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 5DDBB6C322E5C89F00BEA421 /* ShareInspector.app */, 103 | 5DDBB6EF22E5CA6400BEA421 /* ShareExtension.appex */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 5DDBB6C522E5C89F00BEA421 /* ShareInspector */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 5DDBB6CD22E5C8A000BEA421 /* Assets.xcassets */, 112 | 5DDBB6C622E5C89F00BEA421 /* AppDelegate.swift */, 113 | 5DDBB6D222E5C8A000BEA421 /* Info.plist */, 114 | 5DDBB6CF22E5C8A000BEA421 /* LaunchScreen.storyboard */, 115 | 5DDBB6CA22E5C89F00BEA421 /* Main.storyboard */, 116 | 5DDBB6C822E5C89F00BEA421 /* ViewController.swift */, 117 | ); 118 | path = ShareInspector; 119 | sourceTree = ""; 120 | }; 121 | 5DDBB6F022E5CA6400BEA421 /* ShareExtension */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 5D0A79E022E9F02D007C84B1 /* DictionaryCell.swift */, 125 | 5D0A79E222EA28A9007C84B1 /* ImageCell.swift */, 126 | 5DDBB6F122E5CA6400BEA421 /* ShareViewController.swift */, 127 | 5D74E33A22E8571100EFF3AA /* ShareViewModel.swift */, 128 | 5D0A79E422EAE522007C84B1 /* String+Utility.swift */, 129 | 5D74E34122E8D60300EFF3AA /* TextCell.swift */, 130 | 5D74E34322E8D95300EFF3AA /* UITableViewModel.swift */, 131 | 5DDBB6F322E5CA6400BEA421 /* MainInterface.storyboard */, 132 | 5DDBB6F622E5CA6400BEA421 /* Info.plist */, 133 | ); 134 | path = ShareExtension; 135 | sourceTree = ""; 136 | }; 137 | /* End PBXGroup section */ 138 | 139 | /* Begin PBXNativeTarget section */ 140 | 5DDBB6C222E5C89F00BEA421 /* ShareInspector */ = { 141 | isa = PBXNativeTarget; 142 | buildConfigurationList = 5DDBB6D522E5C8A000BEA421 /* Build configuration list for PBXNativeTarget "ShareInspector" */; 143 | buildPhases = ( 144 | 5DDBB6BF22E5C89F00BEA421 /* Sources */, 145 | 5DDBB6C022E5C89F00BEA421 /* Frameworks */, 146 | 5DDBB6C122E5C89F00BEA421 /* Resources */, 147 | 5DDBB6FD22E5CA6400BEA421 /* Embed App Extensions */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | 5DDBB6F822E5CA6400BEA421 /* PBXTargetDependency */, 153 | ); 154 | name = ShareInspector; 155 | productName = ShareInspector; 156 | productReference = 5DDBB6C322E5C89F00BEA421 /* ShareInspector.app */; 157 | productType = "com.apple.product-type.application"; 158 | }; 159 | 5DDBB6EE22E5CA6400BEA421 /* ShareExtension */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = 5DDBB6FC22E5CA6400BEA421 /* Build configuration list for PBXNativeTarget "ShareExtension" */; 162 | buildPhases = ( 163 | 5DDBB6EB22E5CA6400BEA421 /* Sources */, 164 | 5DDBB6EC22E5CA6400BEA421 /* Frameworks */, 165 | 5DDBB6ED22E5CA6400BEA421 /* Resources */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = ShareExtension; 172 | productName = ShareExtension; 173 | productReference = 5DDBB6EF22E5CA6400BEA421 /* ShareExtension.appex */; 174 | productType = "com.apple.product-type.app-extension"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | 5DDBB6BB22E5C89F00BEA421 /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastSwiftUpdateCheck = 1020; 183 | LastUpgradeCheck = 1020; 184 | ORGANIZATIONNAME = "Ole Begemann"; 185 | TargetAttributes = { 186 | 5DDBB6C222E5C89F00BEA421 = { 187 | CreatedOnToolsVersion = 10.2.1; 188 | }; 189 | 5DDBB6EE22E5CA6400BEA421 = { 190 | CreatedOnToolsVersion = 10.2.1; 191 | }; 192 | }; 193 | }; 194 | buildConfigurationList = 5DDBB6BE22E5C89F00BEA421 /* Build configuration list for PBXProject "ShareInspector" */; 195 | compatibilityVersion = "Xcode 9.3"; 196 | developmentRegion = en; 197 | hasScannedForEncodings = 0; 198 | knownRegions = ( 199 | en, 200 | Base, 201 | ); 202 | mainGroup = 5DDBB6BA22E5C89F00BEA421; 203 | productRefGroup = 5DDBB6C422E5C89F00BEA421 /* Products */; 204 | projectDirPath = ""; 205 | projectRoot = ""; 206 | targets = ( 207 | 5DDBB6C222E5C89F00BEA421 /* ShareInspector */, 208 | 5DDBB6EE22E5CA6400BEA421 /* ShareExtension */, 209 | ); 210 | }; 211 | /* End PBXProject section */ 212 | 213 | /* Begin PBXResourcesBuildPhase section */ 214 | 5DDBB6C122E5C89F00BEA421 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 5DDBB6D122E5C8A000BEA421 /* LaunchScreen.storyboard in Resources */, 219 | 5DDBB6CE22E5C8A000BEA421 /* Assets.xcassets in Resources */, 220 | 5DDBB6CC22E5C89F00BEA421 /* Main.storyboard in Resources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | 5DDBB6ED22E5CA6400BEA421 /* Resources */ = { 225 | isa = PBXResourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | 5DDBB6F522E5CA6400BEA421 /* MainInterface.storyboard in Resources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXResourcesBuildPhase section */ 233 | 234 | /* Begin PBXSourcesBuildPhase section */ 235 | 5DDBB6BF22E5C89F00BEA421 /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 5DDBB6C922E5C89F00BEA421 /* ViewController.swift in Sources */, 240 | 5DDBB6C722E5C89F00BEA421 /* AppDelegate.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | 5DDBB6EB22E5CA6400BEA421 /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 5D74E34422E8D95300EFF3AA /* UITableViewModel.swift in Sources */, 249 | 5D0A79E122E9F02D007C84B1 /* DictionaryCell.swift in Sources */, 250 | 5D74E34222E8D60300EFF3AA /* TextCell.swift in Sources */, 251 | 5DDBB6F222E5CA6400BEA421 /* ShareViewController.swift in Sources */, 252 | 5D0A79E522EAE522007C84B1 /* String+Utility.swift in Sources */, 253 | 5D74E33B22E8571100EFF3AA /* ShareViewModel.swift in Sources */, 254 | 5D0A79E322EA28A9007C84B1 /* ImageCell.swift in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXSourcesBuildPhase section */ 259 | 260 | /* Begin PBXTargetDependency section */ 261 | 5DDBB6F822E5CA6400BEA421 /* PBXTargetDependency */ = { 262 | isa = PBXTargetDependency; 263 | target = 5DDBB6EE22E5CA6400BEA421 /* ShareExtension */; 264 | targetProxy = 5DDBB6F722E5CA6400BEA421 /* PBXContainerItemProxy */; 265 | }; 266 | /* End PBXTargetDependency section */ 267 | 268 | /* Begin PBXVariantGroup section */ 269 | 5DDBB6CA22E5C89F00BEA421 /* Main.storyboard */ = { 270 | isa = PBXVariantGroup; 271 | children = ( 272 | 5DDBB6CB22E5C89F00BEA421 /* Base */, 273 | ); 274 | name = Main.storyboard; 275 | sourceTree = ""; 276 | }; 277 | 5DDBB6CF22E5C8A000BEA421 /* LaunchScreen.storyboard */ = { 278 | isa = PBXVariantGroup; 279 | children = ( 280 | 5DDBB6D022E5C8A000BEA421 /* Base */, 281 | ); 282 | name = LaunchScreen.storyboard; 283 | sourceTree = ""; 284 | }; 285 | 5DDBB6F322E5CA6400BEA421 /* MainInterface.storyboard */ = { 286 | isa = PBXVariantGroup; 287 | children = ( 288 | 5DDBB6F422E5CA6400BEA421 /* Base */, 289 | ); 290 | name = MainInterface.storyboard; 291 | sourceTree = ""; 292 | }; 293 | /* End PBXVariantGroup section */ 294 | 295 | /* Begin XCBuildConfiguration section */ 296 | 5DDBB6D322E5C8A000BEA421 /* Debug */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ALWAYS_SEARCH_USER_PATHS = NO; 300 | CLANG_ANALYZER_NONNULL = YES; 301 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 302 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 303 | CLANG_CXX_LIBRARY = "libc++"; 304 | CLANG_ENABLE_MODULES = YES; 305 | CLANG_ENABLE_OBJC_ARC = YES; 306 | CLANG_ENABLE_OBJC_WEAK = YES; 307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_COMMA = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 313 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INFINITE_RECURSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 319 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 323 | CLANG_WARN_STRICT_PROTOTYPES = YES; 324 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 325 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 326 | CLANG_WARN_UNREACHABLE_CODE = YES; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | CODE_SIGN_IDENTITY = "iPhone Developer"; 329 | COPY_PHASE_STRIP = NO; 330 | DEBUG_INFORMATION_FORMAT = dwarf; 331 | ENABLE_STRICT_OBJC_MSGSEND = YES; 332 | ENABLE_TESTABILITY = YES; 333 | GCC_C_LANGUAGE_STANDARD = gnu11; 334 | GCC_DYNAMIC_NO_PIC = NO; 335 | GCC_NO_COMMON_BLOCKS = YES; 336 | GCC_OPTIMIZATION_LEVEL = 0; 337 | GCC_PREPROCESSOR_DEFINITIONS = ( 338 | "DEBUG=1", 339 | "$(inherited)", 340 | ); 341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 343 | GCC_WARN_UNDECLARED_SELECTOR = YES; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 348 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 349 | MTL_FAST_MATH = YES; 350 | ONLY_ACTIVE_ARCH = YES; 351 | SDKROOT = iphoneos; 352 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 353 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 354 | }; 355 | name = Debug; 356 | }; 357 | 5DDBB6D422E5C8A000BEA421 /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 363 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 364 | CLANG_CXX_LIBRARY = "libc++"; 365 | CLANG_ENABLE_MODULES = YES; 366 | CLANG_ENABLE_OBJC_ARC = YES; 367 | CLANG_ENABLE_OBJC_WEAK = YES; 368 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 369 | CLANG_WARN_BOOL_CONVERSION = YES; 370 | CLANG_WARN_COMMA = YES; 371 | CLANG_WARN_CONSTANT_CONVERSION = YES; 372 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 373 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 374 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 375 | CLANG_WARN_EMPTY_BODY = YES; 376 | CLANG_WARN_ENUM_CONVERSION = YES; 377 | CLANG_WARN_INFINITE_RECURSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 380 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 381 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 384 | CLANG_WARN_STRICT_PROTOTYPES = YES; 385 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 386 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 387 | CLANG_WARN_UNREACHABLE_CODE = YES; 388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 389 | CODE_SIGN_IDENTITY = "iPhone Developer"; 390 | COPY_PHASE_STRIP = NO; 391 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 392 | ENABLE_NS_ASSERTIONS = NO; 393 | ENABLE_STRICT_OBJC_MSGSEND = YES; 394 | GCC_C_LANGUAGE_STANDARD = gnu11; 395 | GCC_NO_COMMON_BLOCKS = YES; 396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 398 | GCC_WARN_UNDECLARED_SELECTOR = YES; 399 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 400 | GCC_WARN_UNUSED_FUNCTION = YES; 401 | GCC_WARN_UNUSED_VARIABLE = YES; 402 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 403 | MTL_ENABLE_DEBUG_INFO = NO; 404 | MTL_FAST_MATH = YES; 405 | SDKROOT = iphoneos; 406 | SWIFT_COMPILATION_MODE = wholemodule; 407 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 408 | VALIDATE_PRODUCT = YES; 409 | }; 410 | name = Release; 411 | }; 412 | 5DDBB6D622E5C8A000BEA421 /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 417 | CODE_SIGN_STYLE = Automatic; 418 | DEVELOPMENT_TEAM = B8YY74J254; 419 | INFOPLIST_FILE = ShareInspector/Info.plist; 420 | LD_RUNPATH_SEARCH_PATHS = ( 421 | "$(inherited)", 422 | "@executable_path/Frameworks", 423 | ); 424 | PRODUCT_BUNDLE_IDENTIFIER = com.cocologics.ShareInspector; 425 | PRODUCT_NAME = "$(TARGET_NAME)"; 426 | SWIFT_VERSION = 5.0; 427 | TARGETED_DEVICE_FAMILY = "1,2"; 428 | }; 429 | name = Debug; 430 | }; 431 | 5DDBB6D722E5C8A000BEA421 /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | CODE_SIGN_STYLE = Automatic; 437 | DEVELOPMENT_TEAM = B8YY74J254; 438 | INFOPLIST_FILE = ShareInspector/Info.plist; 439 | LD_RUNPATH_SEARCH_PATHS = ( 440 | "$(inherited)", 441 | "@executable_path/Frameworks", 442 | ); 443 | PRODUCT_BUNDLE_IDENTIFIER = com.cocologics.ShareInspector; 444 | PRODUCT_NAME = "$(TARGET_NAME)"; 445 | SWIFT_VERSION = 5.0; 446 | TARGETED_DEVICE_FAMILY = "1,2"; 447 | }; 448 | name = Release; 449 | }; 450 | 5DDBB6FA22E5CA6400BEA421 /* Debug */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | CODE_SIGN_STYLE = Automatic; 454 | DEVELOPMENT_TEAM = B8YY74J254; 455 | INFOPLIST_FILE = ShareExtension/Info.plist; 456 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 457 | LD_RUNPATH_SEARCH_PATHS = ( 458 | "$(inherited)", 459 | "@executable_path/Frameworks", 460 | "@executable_path/../../Frameworks", 461 | ); 462 | PRODUCT_BUNDLE_IDENTIFIER = com.cocologics.ShareInspector.ShareExtension; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | SKIP_INSTALL = YES; 465 | SWIFT_VERSION = 5.0; 466 | TARGETED_DEVICE_FAMILY = "1,2"; 467 | }; 468 | name = Debug; 469 | }; 470 | 5DDBB6FB22E5CA6400BEA421 /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | CODE_SIGN_STYLE = Automatic; 474 | DEVELOPMENT_TEAM = B8YY74J254; 475 | INFOPLIST_FILE = ShareExtension/Info.plist; 476 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 477 | LD_RUNPATH_SEARCH_PATHS = ( 478 | "$(inherited)", 479 | "@executable_path/Frameworks", 480 | "@executable_path/../../Frameworks", 481 | ); 482 | PRODUCT_BUNDLE_IDENTIFIER = com.cocologics.ShareInspector.ShareExtension; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | SKIP_INSTALL = YES; 485 | SWIFT_VERSION = 5.0; 486 | TARGETED_DEVICE_FAMILY = "1,2"; 487 | }; 488 | name = Release; 489 | }; 490 | /* End XCBuildConfiguration section */ 491 | 492 | /* Begin XCConfigurationList section */ 493 | 5DDBB6BE22E5C89F00BEA421 /* Build configuration list for PBXProject "ShareInspector" */ = { 494 | isa = XCConfigurationList; 495 | buildConfigurations = ( 496 | 5DDBB6D322E5C8A000BEA421 /* Debug */, 497 | 5DDBB6D422E5C8A000BEA421 /* Release */, 498 | ); 499 | defaultConfigurationIsVisible = 0; 500 | defaultConfigurationName = Release; 501 | }; 502 | 5DDBB6D522E5C8A000BEA421 /* Build configuration list for PBXNativeTarget "ShareInspector" */ = { 503 | isa = XCConfigurationList; 504 | buildConfigurations = ( 505 | 5DDBB6D622E5C8A000BEA421 /* Debug */, 506 | 5DDBB6D722E5C8A000BEA421 /* Release */, 507 | ); 508 | defaultConfigurationIsVisible = 0; 509 | defaultConfigurationName = Release; 510 | }; 511 | 5DDBB6FC22E5CA6400BEA421 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = { 512 | isa = XCConfigurationList; 513 | buildConfigurations = ( 514 | 5DDBB6FA22E5CA6400BEA421 /* Debug */, 515 | 5DDBB6FB22E5CA6400BEA421 /* Release */, 516 | ); 517 | defaultConfigurationIsVisible = 0; 518 | defaultConfigurationName = Release; 519 | }; 520 | /* End XCConfigurationList section */ 521 | }; 522 | rootObject = 5DDBB6BB22E5C89F00BEA421 /* Project object */; 523 | } 524 | -------------------------------------------------------------------------------- /ShareInspector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ShareInspector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ShareInspector/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | var window: UIWindow? 6 | 7 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 8 | return true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "idiom" : "ipad", 59 | "size" : "20x20", 60 | "scale" : "1x" 61 | }, 62 | { 63 | "idiom" : "ipad", 64 | "size" : "20x20", 65 | "scale" : "2x" 66 | }, 67 | { 68 | "idiom" : "ipad", 69 | "size" : "29x29", 70 | "scale" : "1x" 71 | }, 72 | { 73 | "idiom" : "ipad", 74 | "size" : "29x29", 75 | "scale" : "2x" 76 | }, 77 | { 78 | "idiom" : "ipad", 79 | "size" : "40x40", 80 | "scale" : "1x" 81 | }, 82 | { 83 | "idiom" : "ipad", 84 | "size" : "40x40", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-76x76@1x.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-83.5x83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "Icon-App-iTunes.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-iTunes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/ShareInspector/Assets.xcassets/AppIcon.appiconset/Icon-App-iTunes.png -------------------------------------------------------------------------------- /ShareInspector/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ShareInspector/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 | -------------------------------------------------------------------------------- /ShareInspector/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 | 28 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ShareInspector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Inspector 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ShareInspector/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ViewController: UIViewController {} 4 | -------------------------------------------------------------------------------- /screenshots/Device-PhotosApp-LivePhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/screenshots/Device-PhotosApp-LivePhoto.png -------------------------------------------------------------------------------- /screenshots/Device-Safari-WebPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/screenshots/Device-Safari-WebPage.png -------------------------------------------------------------------------------- /screenshots/Screenshot-PhotosApp-LivePhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/screenshots/Screenshot-PhotosApp-LivePhoto.png -------------------------------------------------------------------------------- /screenshots/Screenshot-Safari-WebPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocologics/ShareInspector/b2fc5338e738fbfc6b83786b6403fb7ef2975901/screenshots/Screenshot-Safari-WebPage.png --------------------------------------------------------------------------------