├── .swift-version
├── docs
└── assets
│ ├── pods.png
│ ├── catalog.png
│ └── tests.png
├── WORKSPACE
├── .jazzy.yaml
├── AUTHORS
├── .clang-format
├── .kokoro
├── example
├── Catalog.xcworkspace
│ └── contents.xcworkspacedata
├── Podfile
├── components
│ └── Resistor
│ │ ├── Resistor.podspec
│ │ ├── src
│ │ ├── Resistor.h
│ │ ├── RESSeriesResistor.h
│ │ ├── RESParallelResistor.h
│ │ ├── RESSeriesResistor.m
│ │ └── RESParallelResistor.m
│ │ ├── tests
│ │ └── unit
│ │ │ └── RESParallelResistorTests.m
│ │ └── examples
│ │ ├── ParallelResistorExample.m
│ │ └── SeriesExample.m
├── CatalogExamples.podspec
├── CatalogUnitTests.podspec
├── catalog
│ ├── UnitTests
│ │ ├── Info.plist
│ │ └── XcodeCrashFix7.2.1.m
│ ├── Catalog
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── Info.plist
│ │ └── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ ├── UnitTests.xcodeproj
│ │ ├── xcshareddata
│ │ │ └── xcschemes
│ │ │ │ └── UnitTests.xcscheme
│ │ └── project.pbxproj
│ └── Catalog.xcodeproj
│ │ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── Catalog.xcscheme
│ │ └── project.pbxproj
└── Podfile.lock
├── src
├── SnapshotByConvention
│ ├── CBCEnvironment.h
│ └── CBCEnvironment.m
├── CatalogByConvention.h
├── SwiftUI
│ └── SwiftUIExampleWrapper.swift
├── private
│ ├── CBCRuntime.h
│ └── CBCRuntime.m
├── CBCCatalogExample.h
├── CBCNodeListViewController.h
└── CBCNodeListViewController.m
├── CatalogByConvention.podspec
├── .travis.yml
├── CONTRIBUTING.md
├── .gitignore
├── BUILD
├── README.md
├── CHANGELOG.md
└── LICENSE
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/docs/assets/pods.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/material-foundation/cocoapods-catalog-by-convention/HEAD/docs/assets/pods.png
--------------------------------------------------------------------------------
/docs/assets/catalog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/material-foundation/cocoapods-catalog-by-convention/HEAD/docs/assets/catalog.png
--------------------------------------------------------------------------------
/docs/assets/tests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/material-foundation/cocoapods-catalog-by-convention/HEAD/docs/assets/tests.png
--------------------------------------------------------------------------------
/WORKSPACE:
--------------------------------------------------------------------------------
1 | git_repository(
2 | name = "build_bazel_rules_apple",
3 | remote = "https://github.com/bazelbuild/rules_apple.git",
4 | commit = "7ea0557",
5 | )
6 |
--------------------------------------------------------------------------------
/.jazzy.yaml:
--------------------------------------------------------------------------------
1 | module: CatalogByConvention
2 | module_version: 1.0.0
3 | umbrella_header: src/CatalogByConvention.h
4 | objc: true
5 | sdk: iphonesimulator
6 | github_url: https://github.com/material-foundation/cocoapods-catalog-by-convention
7 | github_file_prefix: https://github.com/material-foundation/cocoapods-catalog-by-convention/tree/v2.0.0
8 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the list of CocoaPods Catalog by Convention authors for copyright purposes.
2 | #
3 | # This does not necessarily list everyone who has contributed code, since in
4 | # some cases, their employer may be the copyright holder. To see the full list
5 | # of contributors, see the revision history with git log.
6 |
7 | Google Inc.
8 | and other contributors
9 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 |
3 | AllowShortFunctionsOnASingleLine: Inline
4 | AllowShortIfStatementsOnASingleLine: false
5 | AllowShortLoopsOnASingleLine: false
6 | AlwaysBreakBeforeMultilineStrings: false
7 | BinPackParameters: false
8 | ColumnLimit: 100
9 | IndentWrappedFunctionNames: true
10 | ObjCSpaceBeforeProtocolList: true
11 | PointerBindsToType: false
12 | SortIncludes: true
13 |
--------------------------------------------------------------------------------
/.kokoro:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Fail on any error.
4 | set -e
5 |
6 | if [ ! -d .kokoro-ios-runner ]; then
7 | git clone https://github.com/material-foundation/kokoro-ios-runner.git .kokoro-ios-runner
8 | fi
9 |
10 | pushd .kokoro-ios-runner
11 | git fetch > /dev/null
12 | git checkout v2.1.1 > /dev/null
13 | popd
14 |
15 | ./.kokoro-ios-runner/bazel.sh //:CatalogByConvention
16 |
17 | echo "Success!"
18 |
--------------------------------------------------------------------------------
/example/Catalog.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/Podfile:
--------------------------------------------------------------------------------
1 | abstract_target 'Catalog' do
2 | pod 'CatalogByConvention', :path => '../'
3 | workspace 'Catalog.xcworkspace'
4 | use_frameworks!
5 |
6 | pod 'Resistor', :path => 'components/Resistor'
7 |
8 | target "Catalog" do
9 | project 'catalog/Catalog.xcodeproj'
10 |
11 | # Conventions
12 | pod 'CatalogExamples', :path => './'
13 | end
14 |
15 | target "UnitTests" do
16 | project 'catalog/UnitTests.xcodeproj'
17 |
18 | # Conventions
19 | pod 'CatalogUnitTests', :path => './'
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/src/SnapshotByConvention/CBCEnvironment.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | /** Attempts to reduce flakiness by disabling certain features known to cause flakiness. */
4 | FOUNDATION_EXTERN void CBCReduceFlakiness(void);
5 |
6 | /** Enables Bold Text mode as though it had been toggled by Xcode's accessibility overrides. */
7 | FOUNDATION_EXTERN void CBCEnableBoldTextMode(void) API_AVAILABLE(ios(13));
8 |
9 | /** Set dynamic type by a specific @c UIContentSizeCategory value. */
10 | FOUNDATION_EXTERN void CBCSetDynamicType(UIContentSizeCategory sizeCategory) API_AVAILABLE(ios(13));
11 |
--------------------------------------------------------------------------------
/example/components/Resistor/Resistor.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Resistor"
3 | s.version = "1.0.0"
4 | s.summary = "An example component."
5 | s.homepage = "https://github.com/google/catalog-by-convention"
6 | s.authors = "Google Inc."
7 | s.license = 'Apache 2.0'
8 | s.source = { :git => "https://github.com/google/catalog-by-convention.git", :tag => "v#{s.version}" }
9 | s.requires_arc = true
10 |
11 | s.public_header_files = "src/*.h"
12 | s.source_files = "src/*.{h,m,swift}"
13 | s.header_mappings_dir = "src/*"
14 | end
15 |
--------------------------------------------------------------------------------
/example/CatalogExamples.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "CatalogExamples"
3 | s.version = "1.0.0"
4 | s.summary = "Convention for catalog examples."
5 | s.homepage = "https://github.com/material-foundation/cocoapods-catalog-by-convention"
6 | s.authors = "Google Inc."
7 | s.license = 'Apache 2.0'
8 | s.source = { :git => "https://github.com/material-foundation/cocoapods-catalog-by-convention.git", :tag => "v#{s.version}" }
9 | s.requires_arc = true
10 |
11 | # Conventions
12 | s.source_files = 'components/*/examples/*.{h,m,swift}'
13 | s.public_header_files = 'components/*/examples/*.h'
14 | s.resources = ['components/*/examples/resources/*']
15 | end
16 |
--------------------------------------------------------------------------------
/src/CatalogByConvention.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "CBCNodeListViewController.h"
18 |
--------------------------------------------------------------------------------
/example/components/Resistor/src/Resistor.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "RESParallelResistor.h"
18 | #import "RESSeriesResistor.h"
19 |
--------------------------------------------------------------------------------
/example/CatalogUnitTests.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "CatalogUnitTests"
3 | s.version = "1.0.0"
4 | s.summary = "Convention specification for the catalog examples."
5 | s.homepage = "https://github.com/material-foundation/cocoapods-catalog-by-convention"
6 | s.authors = "Google Inc."
7 | s.license = 'Apache 2.0'
8 | s.source = { :git => "https://github.com/material-foundation/cocoapods-catalog-by-convention.git", :tag => "v#{s.version}" }
9 | s.requires_arc = true
10 |
11 | # The conventions
12 | s.source_files = 'components/*/tests/unit/*.{h,m,swift}'
13 | s.resources = ['components/*/tests/unit/resources/*']
14 | s.framework = 'XCTest'
15 |
16 | # Component dependencies
17 | s.dependency 'Resistor'
18 | end
19 |
--------------------------------------------------------------------------------
/CatalogByConvention.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "CatalogByConvention"
3 | s.version = "2.5.2"
4 | s.authors = "Google Inc."
5 | s.summary = "Tools for building a Catalog by Convention."
6 | s.homepage = "https://github.com/material-foundation/cocoapods-catalog-by-convention"
7 | s.license = 'Apache 2.0'
8 | s.source = { :git => "https://github.com/material-foundation/cocoapods-catalog-by-convention.git", :tag => "v#{s.version}" }
9 | s.platform = :ios,:tvos
10 | s.ios.deployment_target = '8.0'
11 | s.tvos.deployment_target = '9.0'
12 | s.requires_arc = true
13 |
14 | s.public_header_files = "src/*.h"
15 | s.source_files = "src/*.{h,m,swift}", "src/private/*.{h,m,swift}"
16 | s.header_mappings_dir = "src"
17 | end
18 |
--------------------------------------------------------------------------------
/example/components/Resistor/src/RESSeriesResistor.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | @interface RESSeriesResistor : NSObject
20 | @end
21 |
--------------------------------------------------------------------------------
/example/components/Resistor/src/RESParallelResistor.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | @interface RESParallelResistor : NSObject
20 | @end
21 |
--------------------------------------------------------------------------------
/example/catalog/UnitTests/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/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - CatalogByConvention (2.3.1)
3 | - CatalogExamples (1.0.0)
4 | - CatalogUnitTests (1.0.0):
5 | - Resistor
6 | - Resistor (1.0.0)
7 |
8 | DEPENDENCIES:
9 | - CatalogByConvention (from `../`)
10 | - CatalogExamples (from `./`)
11 | - CatalogUnitTests (from `./`)
12 | - Resistor (from `components/Resistor`)
13 |
14 | EXTERNAL SOURCES:
15 | CatalogByConvention:
16 | :path: ../
17 | CatalogExamples:
18 | :path: ./
19 | CatalogUnitTests:
20 | :path: ./
21 | Resistor:
22 | :path: components/Resistor
23 |
24 | SPEC CHECKSUMS:
25 | CatalogByConvention: f4b95f8905470807a5022eabd1d3d9ce07f6a66f
26 | CatalogExamples: 7a95e6ea7befbd43c5ceb1427a9b161f6d8fc34e
27 | CatalogUnitTests: 2fbf7f2e894dd3777f11573a7a5314adb1e4fad7
28 | Resistor: a17e39cab5f42993c2b3ede22ce3829b707a9ac8
29 |
30 | PODFILE CHECKSUM: bb59c09c71f8777bbe79af5ae920e3d58849ab41
31 |
32 | COCOAPODS: 1.4.0
33 |
--------------------------------------------------------------------------------
/example/components/Resistor/src/RESSeriesResistor.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "RESSeriesResistor.h"
18 |
19 | @implementation RESSeriesResistor
20 |
21 | - (instancetype)init {
22 | self = [super init];
23 | if (self) {
24 | NSLog(@"Initialized series");
25 | }
26 | return self;
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/example/components/Resistor/src/RESParallelResistor.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "RESParallelResistor.h"
18 |
19 | @implementation RESParallelResistor
20 |
21 | - (instancetype)init {
22 | self = [super init];
23 | if (self) {
24 | NSLog(@"Initialized parallel");
25 | }
26 | return self;
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode8.1
3 | sudo: false
4 | notifications:
5 | email: false
6 | env:
7 | global:
8 | - LC_CTYPE=en_US.UTF-8
9 | - LANG=en_US.UTF-8
10 | matrix:
11 | - DESTINATION="OS=9.3,name=iPhone 6s Plus"
12 | - DESTINATION="OS=9.2,name=iPhone 6 Plus"
13 | - DESTINATION="OS=9.1,name=iPhone 6s"
14 | - DESTINATION="OS=9.0,name=iPhone 6 Plus"
15 | - DESTINATION="OS=8.4,name=iPhone 6"
16 | - DESTINATION="OS=8.3,name=iPhone 5S"
17 | - DESTINATION="OS=8.2,name=iPhone 5"
18 | - DESTINATION="OS=8.1,name=iPhone 4s"
19 | before_install:
20 | - gem install cocoapods --no-rdoc --no-ri --no-document --quiet
21 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet
22 | - pod install --project-directory=example/
23 | script:
24 | - set -o pipefail
25 | - xcodebuild -workspace example/Catalog.xcworkspace -scheme Catalog -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c;
26 |
--------------------------------------------------------------------------------
/example/components/Resistor/tests/unit/RESParallelResistorTests.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import
18 | #import "Resistor.h"
19 |
20 | @interface RESParallelResitorTests : XCTestCase
21 | @end
22 |
23 | @implementation RESParallelResitorTests
24 |
25 | - (void)testCreatingResistor {
26 | RESParallelResistor *resistor = [RESParallelResistor new];
27 | XCTAssertNotNil(resistor);
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/example/catalog/UnitTests/XcodeCrashFix7.2.1.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | //
18 | // This file is here solely to keep the simulator from crashing on Xcode Version 7.2.1 (7C1002).
19 | // Without this file, the following error occurs when attempting to launch the tests:
20 | //
21 | // Test target encountered an error (Early unexpected exit, operation never finished
22 | // bootstrapping - no restart will be attempted)
23 | //
24 |
--------------------------------------------------------------------------------
/src/SwiftUI/SwiftUIExampleWrapper.swift:
--------------------------------------------------------------------------------
1 | #if canImport(SwiftUI)
2 | import SwiftUI
3 | import UIKit
4 |
5 | /// A view controller to host SwiftUI views wrapped by a UIHostingControllers.
6 | /// Sample use, where MySwiftUIExample is a SwiftUI View:
7 | ///
8 | /// class MySwiftUIExampleWrapper: SwiftUIExampleWrapper {
9 | /// override func viewDidLoad() {
10 | /// super.viewDidLoad()
11 | /// addChildHostingController(UIHostingController(rootView: MySwiftUIExample()))
12 | /// }
13 | /// }
14 | open class SwiftUIExampleWrapper: UIViewController {
15 | public func addChildHostingController(_ swiftUIHostingController: UIViewController) {
16 | swiftUIHostingController.view.translatesAutoresizingMaskIntoConstraints = false
17 | addChild(swiftUIHostingController)
18 | view.addSubview(swiftUIHostingController.view)
19 | swiftUIHostingController.didMove(toParent: self)
20 |
21 | swiftUIHostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive =
22 | true
23 | swiftUIHostingController.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
24 | swiftUIHostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive =
25 | true
26 | swiftUIHostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive =
27 | true
28 | }
29 | }
30 | #endif
31 |
--------------------------------------------------------------------------------
/example/catalog/Catalog/Assets.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 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/example/catalog/Catalog/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | import CatalogByConvention
18 | import UIKit
19 |
20 | @main
21 | class AppDelegate: UIResponder, UIApplicationDelegate {
22 |
23 | var window: UIWindow?
24 |
25 | func application(
26 | _ application: UIApplication,
27 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
28 | ) -> Bool {
29 | self.window = UIWindow(frame: UIScreen.main.bounds)
30 |
31 | let rootViewController = CBCNodeListViewController(node: CBCCreateNavigationTree())
32 | rootViewController.title = "Catalog by Convention"
33 |
34 | let navController = UINavigationController(rootViewController: rootViewController)
35 | self.window?.rootViewController = navController
36 |
37 | self.window!.makeKeyAndVisible()
38 | return true
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example/components/Resistor/examples/ParallelResistorExample.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "Resistor.h"
18 |
19 | #import
20 |
21 | @interface ParallelResistorExample : UIViewController
22 | @end
23 |
24 | @implementation ParallelResistorExample
25 |
26 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
27 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
28 | if (self) {
29 | self.title = @"Parallel resistors";
30 | }
31 | return self;
32 | }
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 |
37 | self.view.backgroundColor = [UIColor whiteColor];
38 | }
39 |
40 | @end
41 |
42 | @implementation ParallelResistorExample (CatalogByConvention)
43 |
44 | + (NSArray *)catalogBreadcrumbs {
45 | return @[ @"Resistor", @"Parallel" ];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/example/components/Resistor/examples/SeriesExample.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "Resistor.h"
18 |
19 | #import
20 |
21 | @interface SeriesExample : UIViewController
22 | @end
23 |
24 | @implementation SeriesExample
25 |
26 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
27 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
28 | if (self) {
29 | self.title = @"Series";
30 | }
31 | return self;
32 | }
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 |
37 | self.view.backgroundColor = [UIColor whiteColor];
38 | }
39 |
40 | @end
41 |
42 | @implementation SeriesExample (CatalogByConvention)
43 |
44 | + (NSArray *> *)catalogBreadcrumbs {
45 | return @[ @[ @"Resistor", @"Series"], @[ @"Film", @"Series" ], @[@"Botany", @"Series"] ];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Want to contribute? Great! First, read this page (including the small print at
2 | the end).
3 |
4 | ### Before you contribute
5 |
6 | Before we can use your code, you must sign the
7 | [Google Individual Contributor License Agreement]
8 | (https://cla.developers.google.com/about/google-individual)
9 | (CLA), which you can do online. The CLA is necessary mainly because you own the
10 | copyright to your changes, even after your contribution becomes part of our
11 | codebase, so we need your permission to use and distribute your code. We also
12 | need to be sure of various other things—for instance that you'll tell us if you
13 | know that your code infringes on other people's patents. You don't have to sign
14 | the CLA until after you've submitted your code for review and a member has
15 | approved it, but you must do it before we can put your code into our codebase.
16 | Before you start working on a larger contribution, you should get in touch with
17 | us first through the issue tracker with your idea so that we can help out and
18 | possibly guide you. Coordinating up front makes it much easier to avoid
19 | frustration later on.
20 |
21 | ### Code reviews
22 |
23 | All submissions, including submissions by project members, require review.
24 | We use GitHub pull requests for this purpose.
25 |
26 | ### The small print
27 |
28 | Contributions made by corporations are covered by a different agreement than
29 | the one above, the
30 | [Software Grant and Corporate Contributor License Agreement]
31 | (https://cla.developers.google.com/about/google-corporate).
32 |
--------------------------------------------------------------------------------
/example/catalog/Catalog/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 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | docs/
2 | bazel-*
3 | .kokoro-ios-runner
4 |
5 | # Xcode
6 | #
7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
8 |
9 | ## Build generated
10 | build/
11 | DerivedData/
12 |
13 | ## Various settings
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata/
23 |
24 | ## Other
25 | *.moved-aside
26 | *.xcuserstate
27 |
28 | ## Obj-C/Swift specific
29 | *.hmap
30 | *.ipa
31 | *.dSYM.zip
32 | *.dSYM
33 |
34 | # CocoaPods
35 | #
36 | # We recommend against adding the Pods directory to your .gitignore. However
37 | # you should judge for yourself, the pros and cons are mentioned at:
38 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
39 | #
40 | Pods/
41 |
42 | # Carthage
43 | #
44 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
45 | # Carthage/Checkouts
46 |
47 | Carthage/Build
48 |
49 | # fastlane
50 | #
51 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
52 | # screenshots whenever they are needed.
53 | # For more information about the recommended setup visit:
54 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
55 |
56 | fastlane/report.xml
57 | fastlane/screenshots
58 |
59 | #Code Injection
60 | #
61 | # After new code Injection tools there's a generated folder /iOSInjectionProject
62 | # https://github.com/johnno1962/injectionforxcode
63 |
64 | iOSInjectionProject/
65 |
--------------------------------------------------------------------------------
/BUILD:
--------------------------------------------------------------------------------
1 | # Description:
2 | # Tools for building a Catalog by Convention.
3 |
4 | licenses(["notice"]) # Apache 2.0
5 |
6 | exports_files(["LICENSE"])
7 |
8 | objc_library(
9 | name = "CatalogByConvention",
10 | srcs = glob([
11 | "src/*.m",
12 | "src/private/*.m",
13 | ]),
14 | hdrs = glob([
15 | "src/*.h",
16 | "src/private/*.h",
17 | ]),
18 | includes = ["src"],
19 | visibility = ["//visibility:public"],
20 | copts = [
21 | "-Wall", # Standard known-to-be-bugs warnings.
22 | "-Wcast-align", # Casting a pointer such that alignment is broken.
23 | "-Wconversion", # Numeric conversion warnings.
24 | "-Wdocumentation", # Documentation checks.
25 | "-Werror", # All warnings as errors.
26 | "-Wextra", # Many useful extra warnings.
27 | "-Wimplicit-atomic-properties", # Dynamic properties should be non-atomic.
28 | "-Wmissing-prototypes", # Global function is defined without a previous prototype.
29 | "-Wno-error=deprecated", # Deprecation warnings are never errors.
30 | "-Wno-error=deprecated-implementations", # Deprecation warnings are never errors.
31 | "-Wno-sign-conversion", # Do not warn on sign conversions.
32 | "-Wno-unused-parameter", # Do not warn on unused parameters.
33 | "-Woverlength-strings", # Strings longer than the C maximum.
34 | "-Wshadow", # Local variable shadows another variable, parameter, etc.
35 | "-Wstrict-selector-match", # Compiler can't figure out the right selector.
36 | "-Wundeclared-selector", # Compiler doesn't see a selector.
37 | "-Wunreachable-code", # Code will never be reached.
38 | ]
39 | )
40 |
--------------------------------------------------------------------------------
/example/catalog/Catalog/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 |
27 |
28 |
--------------------------------------------------------------------------------
/example/catalog/UnitTests.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/private/CBCRuntime.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import
18 | #import
19 |
20 | #pragma mark Class invocations
21 |
22 | /** Invokes +catalogMetadata on the class and returns the NSDictionary value */
23 | FOUNDATION_EXTERN NSDictionary *CBCCatalogMetadataFromClass(Class aClass);
24 |
25 | /**
26 | Returns NO only if the example implements +minimumOSVersion and returns a version that is less than
27 | the current process's operating system version.
28 | */
29 | FOUNDATION_EXTERN BOOL CBCCanRunClassOnCurrentOperatingSystem(Class aClass);
30 |
31 | #pragma mark Runtime enumeration
32 |
33 | /** Returns all Objective-C and Swift classes available to the runtime. */
34 | FOUNDATION_EXTERN NSArray *CBCGetAllCompatibleClasses(void);
35 |
36 | /** Returns an array of classes that respond to a given static method selector. */
37 | FOUNDATION_EXTERN NSArray *CBCClassesRespondingToSelector(NSArray *classes,
38 | SEL selector);
39 |
40 | /**
41 | Internal helper method that allows invoking aClass with selector and puts
42 | the return value in retValue.
43 | */
44 | void CBCCatalogInvokeFromClassAndSelector(Class aClass, SEL selector, void *retValue);
45 |
46 | #pragma mark UIViewController instantiation
47 |
48 | /**
49 | Creates a view controller instance from the provided class.
50 |
51 | If the provided class implements +(NSString *)catalogStoryboardName, a UIStoryboard instance will
52 | be created with the returned name. The returned view controller will be instantiated by invoking
53 | -instantiateInitialViewController on the UIStoryboard instance.
54 | */
55 | FOUNDATION_EXTERN UIViewController *CBCViewControllerFromClass(Class aClass, NSDictionary *metadata);
56 |
57 | #pragma mark Fix View Debugging
58 |
59 | /**
60 | Fixes View Debugging in Xcode when running on iOS 8 and below. See
61 | http://stackoverflow.com/questions/36313850/debug-view-hierarchy-in-xcode-7-3-fails
62 | */
63 | FOUNDATION_EXTERN void CBCFixViewDebuggingIfNeeded(void);
64 |
--------------------------------------------------------------------------------
/src/CBCCatalogExample.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | /**
20 | The CBCCatalogExample protocol defines methods that examples are expected to implement in order to
21 | customize their location and behavior in the Catalog by Convention.
22 |
23 | Examples should not formally conform to this protocol. Examples should simply implement these
24 | methods by convention.
25 | */
26 | @protocol CBCCatalogExample
27 |
28 | /**
29 | Returns a dictionary with metaata information for the example.
30 | */
31 | + (nonnull NSDictionary *)catalogMetadata;
32 |
33 | @optional
34 |
35 | /** Return a list of breadcrumbs defining the navigation path taken to reach this example. */
36 | + (nonnull NSArray *)catalogBreadcrumbs
37 | __attribute__((deprecated("use catalogMetadata[CBCBreadcrumbs] instead.")));
38 |
39 | /**
40 | Return a BOOL stating whether this example should be treated as the primary demo of the component.
41 | */
42 | + (BOOL)catalogIsPrimaryDemo
43 | __attribute__((deprecated("use catalogMetadata[CBCIsPrimaryDemo] instead.")));;
44 |
45 | /**
46 | Return a BOOL stating whether this example is presentable and should be part of the catalog app.
47 | */
48 | + (BOOL)catalogIsPresentable
49 | __attribute__((deprecated("use catalogMetadata[CBCIsPresentable] instead.")));
50 |
51 | /**
52 | Return a BOOL stating whether this example is in debug mode and should appear as the initial view controller.
53 | */
54 | + (BOOL)catalogIsDebug
55 | __attribute__((deprecated("use catalogMetadata[CBCIsDebug] instead.")));
56 |
57 | /**
58 | Return the name of a UIStoryboard from which the example's view controller should be instantiated.
59 | */
60 | - (nonnull NSString *)catalogStoryboardName
61 | __attribute__((deprecated("use catalogMetadata[CBCStoryboardName] instead.")));
62 |
63 | /**
64 | Return the minimum OS version that this example supports being ran on.
65 |
66 | If this method is not implemented, then it's assumed that the example can run on any OS version
67 | lower than the `deprecatedOSVersion`, if specified.
68 | */
69 | - (NSOperatingSystemVersion)minimumOSVersion;
70 |
71 | /**
72 | Return the OS version that this example is deprecated at.
73 |
74 | If this method is not implemented, then it's assumed that the example can run on any OS version
75 | larger than or equal to the `minimumOSVersion`, if specified.
76 | */
77 | - (NSOperatingSystemVersion)deprecatedOSVersion;
78 |
79 | /** Return a description of the example. */
80 | - (nonnull NSString *)catalogDescription
81 | __attribute__((deprecated("use catalogMetadata[CBCDescription] instead.")));
82 |
83 | /** Return a link to related information or resources. */
84 | - (nonnull NSURL *)catalogRelatedInfo
85 | __attribute__((deprecated("use catalogMetadata[CBCRelatedInfo] instead.")));
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/example/catalog/Catalog.xcodeproj/xcshareddata/xcschemes/Catalog.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/src/CBCNodeListViewController.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import
18 |
19 | /** This key represents a strings array of the breadcrumbs showing the hierarchy of the example */
20 | FOUNDATION_EXTERN NSString *_Nonnull const CBCBreadcrumbs;
21 | /** This key represents a boolean value if the example is for debugging */
22 | FOUNDATION_EXTERN NSString *_Nonnull const CBCIsDebug;
23 | /** This key represents a string for the description for the example */
24 | FOUNDATION_EXTERN NSString *_Nonnull const CBCDescription;
25 | /** This key represents a boolean value if to present the example in the Catalog app or not */
26 | FOUNDATION_EXTERN NSString *_Nonnull const CBCIsPresentable;
27 | /** This key represents a boolean value if the example is the primary demo */
28 | FOUNDATION_EXTERN NSString *_Nonnull const CBCIsPrimaryDemo;
29 | /** This key represents an NSURL value providing related info for the example */
30 | FOUNDATION_EXTERN NSString *_Nonnull const CBCRelatedInfo;
31 | /** This key represents a string value of the storyboard name for the example */
32 | FOUNDATION_EXTERN NSString *_Nonnull const CBCStoryboardName;
33 | /** This key represents an NSArray of strings for the keywords for the example */
34 | FOUNDATION_EXTERN NSString *_Nonnull const CBCKeywords;
35 |
36 | @class CBCNode;
37 |
38 | /**
39 | An instance of CBCNodeListViewController is able to represent a non-example CBCNode instance as a
40 | UITableView.
41 | */
42 | @interface CBCNodeListViewController : UIViewController
43 |
44 | /** Initializes a CBCNodeViewController instance with a non-example node. */
45 | - (nonnull instancetype)initWithNode:(nonnull CBCNode *)node;
46 |
47 | - (nonnull instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
48 |
49 | @property(nonatomic, strong, nonnull) UITableView *tableView;
50 |
51 | /** The node that this view controller must represent. */
52 | @property(nonatomic, strong, nonnull, readonly) CBCNode *node;
53 |
54 | /** The preferred mechanism for fetching a node for a given index path. Supports grouping. */
55 | - (nonnull CBCNode *)nodeForIndexPath:(nonnull NSIndexPath *)indexPath;
56 |
57 | @property(nonatomic) BOOL searchEnabled;
58 |
59 | - (void)updateFilters:(nonnull NSString *)filter enabled:(BOOL)enabled;
60 |
61 | - (void)findSearchText:(nonnull NSString *)searchtext;
62 |
63 | @end
64 |
65 | /**
66 | Returns the root of a CBCNode tree representing the complete catalog navigation hierarchy.
67 |
68 | Only classes that implement +catalogBreadcrumbs and return at least one breadcrumb will be part of
69 | the tree.
70 | */
71 | FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreateNavigationTree(void);
72 |
73 | /**
74 | Returns the root of a CBCNode tree representing only the presentable catalog navigation hierarchy.
75 |
76 | Only classes that implement +catalogIsPresentable with a return value of YES,
77 | and +catalogBreadcrumbs and return at least one breadcrumb will be part of the tree.
78 | */
79 | FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void);
80 |
81 | /**
82 | A node describes a single navigable page in the Catalog by Convention.
83 |
84 | A node either has children or it is an example.
85 |
86 | - If a node has children, then the node should be represented by a list of some sort.
87 | - If a node is an example, then the example controller can be instantiated with
88 | createExampleViewController.
89 | */
90 | @interface CBCNode : NSObject
91 |
92 | /** Nodes cannot be created by clients. */
93 | - (nonnull instancetype)init NS_UNAVAILABLE;
94 |
95 | /** The title for this node. */
96 | @property(nonatomic, copy, nonnull, readonly) NSString *title;
97 |
98 | /** The children of this node. */
99 | @property(nonatomic, strong, nonnull) NSArray *children;
100 |
101 | /**
102 | The example you wish to debug as the initial view controller.
103 | If there are multiple examples with catalogIsDebug returning YES
104 | the debugLeaf will hold the example that has been iterated on last
105 | in the hierarchy tree.
106 | */
107 | @property(nonatomic, strong, nullable) CBCNode *debugLeaf;
108 |
109 | /**
110 | This NSDictionary holds all the metadata related to this CBCNode.
111 | If it is an example noe, a primary demo, related info,
112 | if presentable in Catalog, etc.
113 | */
114 | @property(nonatomic, strong, nonnull) NSDictionary *metadata;
115 |
116 | /** The group of this node. */
117 | @property(nonatomic, strong, nullable) NSString *group;
118 |
119 | /** Returns YES if this is an example node. */
120 | - (BOOL)isExample;
121 |
122 | /**
123 | Returns YES if this the primary demo for this component.
124 |
125 | Can only return YES if isExample also returns YES.
126 | */
127 | - (BOOL)isPrimaryDemo;
128 |
129 | /** Returns YES if this is a presentable example. */
130 | - (BOOL)isPresentable;
131 |
132 | /** Returns String representation of exampleViewController class name if it exists */
133 | - (nullable NSString *)exampleViewControllerName;
134 |
135 | /**
136 | Returns an instance of a UIViewController for presentation purposes.
137 |
138 | Check that isExample returns YES before invoking.
139 | */
140 | - (nonnull UIViewController *)createExampleViewController;
141 |
142 | /**
143 | Returns a description of the example.
144 |
145 | Check that isExample returns YES before invoking.
146 | */
147 | - (nullable NSString *)exampleDescription;
148 |
149 | /** Returns a link to related information for the example. */
150 | - (nullable NSURL *)exampleRelatedInfo;
151 |
152 | @end
153 |
--------------------------------------------------------------------------------
/src/SnapshotByConvention/CBCEnvironment.m:
--------------------------------------------------------------------------------
1 | #import "CBCEnvironment.h"
2 |
3 | #import
4 | #import
5 |
6 | static BOOL IsSubviewOfUIAlertControllerView(UIView *view) {
7 | static dispatch_once_t onceToken;
8 | static Class UIAlertControllerViewClass = nil;
9 | dispatch_once(&onceToken, ^{
10 | UIAlertControllerViewClass = NSClassFromString(@"_UIAlertControllerView");
11 | });
12 |
13 | UIView *iterator = view;
14 | while (iterator) {
15 | if ([iterator isMemberOfClass:UIAlertControllerViewClass]) {
16 | return YES;
17 | }
18 | iterator = iterator.superview;
19 | }
20 | return NO;
21 | }
22 |
23 | @implementation UIVisualEffectView (CatalogByConventionFlakinessReduction)
24 |
25 | - (void)cbc_setEffect:(UIVisualEffect *)effect {
26 | if (@available(iOS 14, *)) {
27 | // Never allow effects to be set, and if they are set, pin the background color so that the
28 | // view isn't transparent.
29 | [self cbc_setEffect:nil];
30 | self.backgroundColor = [UIColor systemBackgroundColor];
31 | } else {
32 | [self cbc_setEffect:effect];
33 | }
34 | }
35 |
36 | - (void)cbc_addSubview:(UIView *)view {
37 | if (@available(iOS 14, *)) {
38 | static dispatch_once_t onceToken;
39 | static Class UIVisualEffectBackdropViewClass = nil;
40 | dispatch_once(&onceToken, ^{
41 | UIVisualEffectBackdropViewClass = NSClassFromString(@"_UIVisualEffectBackdropView");
42 | });
43 |
44 | if ([view isMemberOfClass:UIVisualEffectBackdropViewClass]) {
45 | // Ignore adding this view. Instead, pin the background of the UIVisualEffectView to a
46 | // solid color.
47 | self.backgroundColor = [UIColor systemBackgroundColor];
48 | return;
49 | }
50 | }
51 |
52 | // Call the pre-swizzled implementation in the general case.
53 | [self cbc_addSubview:view];
54 | }
55 |
56 | @end
57 |
58 | // Technically an extension for _UIDimmingKnockoutBackdropView, but it's a private API so we extend
59 | // UIView instead.
60 | @implementation UIView (CatalogByConventionFlakinessReduction)
61 |
62 | - (void)cbc_setUIDimmingKnockoutBackdropViewCornerRadius:(CGFloat)cornerRadius {
63 | if (@available(iOS 14, *)) {
64 | // To avoid over-disabling corner radii, we only ignore the corner radius if we're a subview of
65 | // a _UIAlertControllerView.
66 | if (IsSubviewOfUIAlertControllerView(self)) {
67 | [self cbc_setUIDimmingKnockoutBackdropViewCornerRadius:0];
68 | return;
69 | }
70 | }
71 |
72 | // Call the pre-swizzled implementation in the general case.
73 | [self cbc_setUIDimmingKnockoutBackdropViewCornerRadius:cornerRadius];
74 | }
75 |
76 | @end
77 |
78 | @implementation CALayer (CatalogByConventionFlakinessReduction)
79 |
80 | - (void)cbc_setCornerRadius:(CGFloat)cornerRadius {
81 | if (@available(iOS 14, *)) {
82 | static dispatch_once_t onceToken;
83 | static Class UIDropShadowViewClass = nil;
84 | dispatch_once(&onceToken, ^{
85 | UIDropShadowViewClass = NSClassFromString(@"UIDropShadowView");
86 | });
87 |
88 | // Disable rounded corners in modally presented view controllers. The rounded corner behavior
89 | // is governed by the subviews of UIDropShadowView, so we check to see if this layer's view is a
90 | // subview of a UIDropShadowView. We're able to get the layer's view by looking at the delegate,
91 | // which will always be set to the corresponding UIView for layer-backed UIViews.
92 | if ([self.delegate isKindOfClass:[UIView class]]) {
93 | UIView *view = (UIView *)self.delegate;
94 | if ([view.superview isMemberOfClass:UIDropShadowViewClass]) {
95 | [self cbc_setCornerRadius:0];
96 | return;
97 | }
98 | }
99 | }
100 |
101 | // Call the pre-swizzled implementation in the general case.
102 | [self cbc_setCornerRadius:cornerRadius];
103 | }
104 |
105 | @end
106 |
107 | // General purpose instance method swizzler.
108 | static void Swizzle(Class aClass, SEL originalSelector, SEL swizzledSelector) {
109 | Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
110 | Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
111 |
112 | BOOL didAddMethod =
113 | class_addMethod(aClass, originalSelector, method_getImplementation(swizzledMethod),
114 | method_getTypeEncoding(swizzledMethod));
115 | if (didAddMethod) {
116 | class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originalMethod),
117 | method_getTypeEncoding(originalMethod));
118 | } else {
119 | method_exchangeImplementations(originalMethod, swizzledMethod);
120 | }
121 | }
122 |
123 | static void DisableBlurEffects(void) {
124 | Swizzle([UIVisualEffectView class], @selector(setEffect:), @selector(cbc_setEffect:));
125 | Swizzle([UIVisualEffectView class], @selector(addSubview:), @selector(cbc_addSubview:));
126 | }
127 |
128 | static void DisableRoundedAlerts(void) {
129 | Swizzle(NSClassFromString(@"_UIDimmingKnockoutBackdropView"), @selector(setCornerRadius:),
130 | @selector(cbc_setUIDimmingKnockoutBackdropViewCornerRadius:));
131 | }
132 |
133 | static void DisableRoundedModalViewControllers(void) {
134 | // We need to swizzle CALayer's setCornerRadius because UIKit's private APIs will repeatedly
135 | // attempt to enforce the corner radius of modal view controllers at various stages of
136 | // presentation, and we want to ensure that the radius is never set.
137 | Swizzle([CALayer class], @selector(setCornerRadius:), @selector(cbc_setCornerRadius:));
138 | }
139 |
140 | #pragma mark - Public APIs
141 |
142 | void CBCReduceFlakiness(void) {
143 | static dispatch_once_t onceToken;
144 | dispatch_once(&onceToken, ^{
145 | if (@available(iOS 14, *)) {
146 | // On iOS 14, blur effects result in an almost 50/50 split in rendering behaviors when taking
147 | // snapshots, so we disable them entirely.
148 | DisableBlurEffects();
149 |
150 | // On iOS 14, rounded corners on alerts cause flakiness about 50% of the time as well.
151 | DisableRoundedAlerts();
152 | DisableRoundedModalViewControllers();
153 | }
154 | });
155 | }
156 |
157 | @protocol AccessibilitySupportOverrides
158 | + (instancetype)shared;
159 | - (instancetype)initWithContentSizeCategory:(UIContentSizeCategory)value;
160 | - (void)setBoldText:(NSNumber *)value;
161 | - (void)overrideSystemWithPreference:(id)value;
162 | @end
163 |
164 | static void SetBoldText(BOOL enabled) {
165 | [[objc_getClass("AccessibilitySupportOverrides") shared] setBoldText:@(enabled)];
166 | }
167 |
168 | void CBCEnableBoldTextMode(void) {
169 | SetBoldText(YES);
170 | }
171 |
172 | void CBCSetDynamicType(UIContentSizeCategory sizeCategory) {
173 | [objc_getClass("UIContentSizeCategoryPreference") overrideSystemWithPreference:[[objc_getClass("UIContentSizeCategoryPreference") alloc] initWithContentSizeCategory:sizeCategory]];
174 | }
175 |
176 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Catalog by Convention
2 |
3 | [](https://travis-ci.org/material-foundation/cocoapods-catalog-by-convention)
4 |
5 | Catalog by Convention is a set of runtime tools and conventions designed for the development of a
6 | collection of Objective-C and Swift "components".
7 |
8 | ## Overview
9 |
10 | If your team works on many components simultaneously then you may find that building examples and
11 | unit tests involves some non-trivial overhead:
12 |
13 | - Maintenance of each component's Xcode projects.
14 | - Maintenance of each component's examples and testing target.
15 | - Switching between projects/targets/workspaces.
16 |
17 | One solution to this problem is to create what's called a "Catalog" app. A Catalog's purpose is to
18 | demonstrate the usage of a collection of components in one app. A Catalog application reduces the
19 | number of distinct Xcode targets your team has to interact with. Your team manages your Catalog's
20 | Xcode project, adding and updating files as necessary.
21 |
22 | What if — instead of managing an Xcode project — you simply had to create new source files and run
23 | `pod install`?
24 |
25 | Catalog by Convention minimizes the engineering overhead of creating **examples** and **unit tests**
26 | using a combination of conventions and CocoaPods. Simply run `pod install` and your Catalog will
27 | include all examples and unit tests in two easy to access targets.
28 |
29 | The only remaining engineering burden is to actually write the examples and tests.
30 |
31 | ### An example
32 |
33 | An example Catalog is located within the `example/` directory. Run pod install to set it up:
34 |
35 | pod install --project-directory=example/
36 | open example/Catalog.xcworkspace
37 |
38 | Open the project and you'll find two targets: Catalog and UnitTests. Run the Catalog.
39 |
40 | 
41 |
42 | You can navigate through to the single component's examples in the catalog application.
43 |
44 | Now try running the unit tests. You'll see the one Resistor test appear in the unit test output.
45 |
46 | 
47 |
48 | **Quick introduction to adding a new example**
49 |
50 | Open the following directory:
51 |
52 | open example/components/Resistor/examples/
53 |
54 | 1. Make a duplicate of either example.
55 | 2. Open the duplicate and give the class a unique name.
56 | 3. Change the `+catalogBreadcrumbs` path to something unique.
57 | 4. Run `pod install --project-directory=example/` and rebuild the app.
58 | 5. Your example will now be listed under Resistor.
59 |
60 | These five steps describe the end-to-end process for adding new examples to the catalog.
61 |
62 | **Quick introduction to adding a new test**
63 |
64 | Open the following directory:
65 |
66 | open example/components/Resistor/tests/unit/
67 |
68 | 1. Make a duplicate of either test.
69 | 2. Open the duplicate and give the class a unique name.
70 | 3. Run `pod install --project-directory=example/` and rebuild the app.
71 | 4. Run the unit tests.
72 |
73 | ## Setup guide
74 |
75 | This guide will walk you through how to create a Catalog project that uses the CatalogByConvention
76 | library.
77 |
78 | ### Step 1: Plan out your component conventions
79 |
80 | This is the most important step. What matters most here is that you apply the convention
81 | consistently across each of your components.
82 |
83 | Let's look at the convention followed by the example included in the `example/` directory:
84 |
85 | components/
86 | ComponentNameCamelCased/
87 | examples/
88 | SomeExample.m
89 | src/
90 | Resistor.h
91 | RESClass.h
92 | RESClass.m
93 | tests/
94 | unit/
95 | SomeUnitTest.m
96 |
97 | ### Step 2: Create the necessary files/folders
98 |
99 | Alongside the `components/` directory we'll create the following:
100 |
101 | - A `catalog/` directory.
102 | - A `CatalogExamples.podspec`
103 | - A `CatalogUnitTests.podspec`
104 | - A `Podfile`
105 |
106 | The final result will look like so:
107 |
108 | catalog/
109 | components/
110 | CatalogExamples.podspec
111 | CatalogUnitTests.podspec
112 | Podfile
113 |
114 | ### Step 3: Create the convention podspecs
115 |
116 | Let's look at the contents of `CatalogExamples.podspec` and `CatalogUnitTests.podspec`.
117 |
118 | Within `CatalogExamples.podspec`:
119 |
120 | Pod::Spec.new do |s|
121 | s.name = "CatalogExamples"
122 | s.version = "1.0.0"
123 | s.summary = "Convention for catalog examples."
124 | s.homepage = "https://github.com/your/repo"
125 | s.authors = "Catalog"
126 | s.license = 'Apache 2.0'
127 | s.source = { :git => "https://github.com/your/repo.git", :tag => s.version.to_s }
128 | s.requires_arc = true
129 |
130 | # Conventions
131 | s.source_files = 'components/*/examples/*.{h,m,swift}'
132 | s.public_header_files = 'components/*/examples/*.h'
133 | s.resources = ['components/*/examples/resources/*']
134 | end
135 |
136 | Within `CatalogUnitTests.podspec`:
137 |
138 | Pod::Spec.new do |s|
139 | s.name = "CatalogUnitTests"
140 | s.version = "1.0.0"
141 | s.summary = "Convention for catalog tests."
142 | s.homepage = "https://github.com/your/repo"
143 | s.authors = "Catalog"
144 | s.license = 'Apache 2.0'
145 | s.source = { :git => "https://github.com/your/repo.git", :tag => s.version.to_s }
146 | s.requires_arc = true
147 | s.framework = 'XCTest'
148 |
149 | # Conventions
150 | s.source_files = 'components/*/tests/unit/*.{h,m,swift}'
151 | s.resources = ['components/*/tests/unit/resources/*']
152 |
153 | # Unit tests require you to specify your components as dependencies.
154 | s.dependency 'Resistor'
155 | end
156 |
157 | ### Step 4: Create a Podfile for your catalog
158 |
159 | Now let's edit the `Podfile`.
160 |
161 | Feel free to use the following as a template, updating the names of targets, paths, and dependencies
162 | where applicable.
163 |
164 | abstract_target 'Catalog' do
165 | workspace 'Catalog.xcworkspace'
166 | use_frameworks!
167 |
168 | pod 'CatalogByConvention'
169 |
170 | # Define where the local pods live. This allows your conventions to depend on them.
171 | pod 'Resistor', :path => 'components/Resistor'
172 |
173 | target "Catalog" do
174 | project 'catalog/Catalog.xcodeproj'
175 | pod 'CatalogExamples', :path => './'
176 | end
177 |
178 | target "UnitTests" do
179 | project 'catalog/Catalog.xcodeproj'
180 | pod 'CatalogUnitTests', :path => './'
181 | end
182 | end
183 |
184 | ### Step 5: Create the Catalog Xcode project
185 |
186 | Create a new Xcode project. We'll assume you're using the "Single View Application" template. Enable
187 | unit tests for the project.
188 |
189 | Ensure that your app and unit test target match those defined in your `Podfile`.
190 |
191 | Delete the default ViewController class.
192 |
193 | Update your app delegate to look like the following:
194 |
195 | import UIKit
196 | import CatalogByConvention
197 |
198 | @UIApplicationMain
199 | class AppDelegate: UIResponder, UIApplicationDelegate {
200 |
201 | var window: UIWindow?
202 |
203 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
204 | self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
205 |
206 | let rootViewController = CBCNodeListViewController(node: CBCCreateNavigationTree())
207 | rootViewController.title = "Catalog by Convention"
208 |
209 | let navController = UINavigationController(rootViewController: rootViewController)
210 | self.window?.rootViewController = navController
211 |
212 | self.window!.makeKeyAndVisible()
213 | return true
214 | }
215 | }
216 |
217 | ### Step 6: Run pod install
218 |
219 | Run `pod install` for your Catalog. Open your Catalog's workspace.
220 |
221 | pod install
222 | open Catalog.xcworkspace
223 |
224 | All of your examples, unit tests, and component source code will be found within the Pods project
225 | in your workspace.
226 |
227 | 
228 |
229 | ### Step 7: Build!
230 |
231 | From this point forward you simply need to create new example and unit test source files and they'll
232 | be picked up on a subsequent pod install.
233 |
234 | ### Ongoing steps: Adding examples
235 |
236 | For an example view controller to appear in your project your view controller must implement
237 | `+catalogBreadcrumbs`. For example:
238 |
239 | @implementation ParallelResistorExample (CatalogByConvention)
240 |
241 | + (NSArray *)catalogBreadcrumbs {
242 | return @[ @"Resistor", @"Parallel" ];
243 | }
244 |
245 | @end
246 |
247 | ## License
248 |
249 | Licensed under the Apache 2.0 license. See LICENSE for details.
250 |
--------------------------------------------------------------------------------
/src/private/CBCRuntime.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "CBCRuntime.h"
18 |
19 | #import "CBCCatalogExample.h"
20 |
21 | #import
22 |
23 | #pragma mark Metadata keys
24 |
25 | NSString *const CBCBreadcrumbs = @"breadcrumbs";
26 | NSString *const CBCIsDebug = @"debug";
27 | NSString *const CBCDescription = @"description";
28 | NSString *const CBCIsPresentable = @"presentable";
29 | NSString *const CBCIsPrimaryDemo = @"primaryDemo";
30 | NSString *const CBCRelatedInfo = @"relatedInfo";
31 | NSString *const CBCStoryboardName = @"storyboardName";
32 | NSString *const CBCMinimumOSVersion = @"minimumOSVersion";
33 | NSString *const CBCDeprecatedOSVersion = @"deprecatedOSVersion";
34 | NSString *const CBCKeywords = @"keywords";
35 |
36 | #pragma mark Class invocations
37 |
38 | static NSArray *CBCCatalogBreadcrumbsFromClass(Class aClass) {
39 | return [aClass performSelector:@selector(catalogBreadcrumbs)];
40 | }
41 |
42 | static BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass) {
43 | BOOL isPrimary = NO;
44 | if ([aClass respondsToSelector:@selector(catalogIsPrimaryDemo)]) {
45 | isPrimary = [aClass catalogIsPrimaryDemo];
46 | }
47 | return isPrimary;
48 | }
49 |
50 | static BOOL CBCCatalogIsPresentableFromClass(Class aClass) {
51 | BOOL isPresentable = YES;
52 | if ([aClass respondsToSelector:@selector(catalogIsPresentable)]) {
53 | isPresentable = [aClass catalogIsPresentable];
54 | }
55 | return isPresentable;
56 | }
57 |
58 | static BOOL CBCCatalogIsDebugLeaf(Class aClass) {
59 | BOOL isDebugLeaf = NO;
60 | if ([aClass respondsToSelector:@selector(catalogIsDebug)]) {
61 | isDebugLeaf = [aClass catalogIsDebug];
62 | }
63 | return isDebugLeaf;
64 | }
65 |
66 | static NSURL *CBCRelatedInfoFromClass(Class aClass) {
67 | NSURL *catalogRelatedInfo = nil;
68 | if ([aClass respondsToSelector:@selector(catalogRelatedInfo)]) {
69 | catalogRelatedInfo = [aClass catalogRelatedInfo];
70 | }
71 | return catalogRelatedInfo;
72 | }
73 |
74 | static NSString *CBCDescriptionFromClass(Class aClass) {
75 | NSString *catalogDescription = nil;
76 | if ([aClass respondsToSelector:@selector(catalogDescription)]) {
77 | catalogDescription = [aClass catalogDescription];
78 | }
79 | return catalogDescription;
80 | }
81 |
82 | static NSString *CBCStoryboardNameFromClass(Class aClass) {
83 | NSString *catalogStoryboardName = nil;
84 | if ([aClass respondsToSelector:@selector(catalogStoryboardName)]) {
85 | catalogStoryboardName = [aClass catalogStoryboardName];
86 | }
87 | return catalogStoryboardName;
88 | }
89 |
90 | BOOL CBCCanRunClassOnCurrentOperatingSystem(Class aClass) {
91 | BOOL osVersionAtLeastMinimum = YES;
92 | BOOL osVersionLessThanDeprecated = YES;
93 | if ([aClass respondsToSelector:@selector(minimumOSVersion)]) {
94 | NSOperatingSystemVersion minimumOSVersion = [aClass minimumOSVersion];
95 |
96 | osVersionAtLeastMinimum =
97 | [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:minimumOSVersion];
98 | }
99 |
100 | if ([aClass respondsToSelector:@selector(deprecatedOSVersion)]) {
101 | NSOperatingSystemVersion deprecatedOSVersion = [aClass deprecatedOSVersion];
102 | osVersionLessThanDeprecated =
103 | ![[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:deprecatedOSVersion];
104 | }
105 |
106 | return osVersionAtLeastMinimum && osVersionLessThanDeprecated;
107 | }
108 |
109 | static NSDictionary *CBCConstructMetadataFromMethods(Class aClass) {
110 | NSMutableDictionary *catalogMetadata = [NSMutableDictionary new];
111 | if ([aClass respondsToSelector:@selector(catalogBreadcrumbs)]) {
112 | [catalogMetadata setObject:CBCCatalogBreadcrumbsFromClass(aClass) forKey:CBCBreadcrumbs];
113 | [catalogMetadata setObject:[NSNumber numberWithBool:CBCCatalogIsPrimaryDemoFromClass(aClass)]
114 | forKey:CBCIsPrimaryDemo];
115 | [catalogMetadata setObject:[NSNumber numberWithBool:CBCCatalogIsPresentableFromClass(aClass)]
116 | forKey:CBCIsPresentable];
117 | [catalogMetadata setObject:[NSNumber numberWithBool:CBCCatalogIsDebugLeaf(aClass)]
118 | forKey:CBCIsDebug];
119 | NSURL *relatedInfo;
120 | if ((relatedInfo = CBCRelatedInfoFromClass(aClass)) != nil) {
121 | [catalogMetadata setObject:CBCRelatedInfoFromClass(aClass) forKey:CBCRelatedInfo];
122 | }
123 | NSString *description;
124 | if ((description = CBCDescriptionFromClass(aClass)) != nil) {
125 | [catalogMetadata setObject:CBCDescriptionFromClass(aClass) forKey:CBCDescription];
126 | }
127 | NSString *storyboardName;
128 | if ((storyboardName = CBCStoryboardNameFromClass(aClass)) != nil) {
129 | [catalogMetadata setObject:CBCStoryboardNameFromClass(aClass) forKey:CBCStoryboardName];
130 | }
131 | }
132 | return catalogMetadata;
133 | }
134 |
135 | NSDictionary *CBCCatalogMetadataFromClass(Class aClass) {
136 | NSDictionary *catalogMetadata;
137 | if ([aClass respondsToSelector:@selector(catalogMetadata)]) {
138 | catalogMetadata = [aClass catalogMetadata];
139 | } else {
140 | catalogMetadata = CBCConstructMetadataFromMethods(aClass);
141 | }
142 | return catalogMetadata;
143 | }
144 |
145 | #pragma mark Runtime enumeration
146 |
147 | static BOOL IsSubclassOfClass(Class aClass, Class parentClass) {
148 | Class iterator = class_getSuperclass(aClass);
149 | while (iterator) {
150 | if (iterator == parentClass) {
151 | return YES;
152 | }
153 | iterator = class_getSuperclass(iterator);
154 | }
155 | return NO;
156 | }
157 |
158 | NSArray *CBCGetAllCompatibleClasses(void) {
159 | int numberOfClasses = objc_getClassList(NULL, 0);
160 | Class *classList = (Class *)malloc((size_t)numberOfClasses * sizeof(Class));
161 | objc_getClassList(classList, numberOfClasses);
162 |
163 | NSMutableArray *classes = [NSMutableArray array];
164 |
165 | NSSet *ignoredClasses = [NSSet setWithArray:@[
166 | @"SwiftObject", @"Object", @"FigIrisAutoTrimmerMotionSampleExport", @"NSLeafProxy"
167 | ]];
168 | NSArray *ignoredPrefixes = @[ @"Swift.", @"_", @"JS", @"WK", @"PF", @"NS" ];
169 |
170 | Class viewControllerClass = [UIViewController class];
171 |
172 | for (int ix = 0; ix < numberOfClasses; ++ix) {
173 | Class aClass = classList[ix];
174 |
175 | if (!IsSubclassOfClass(aClass, viewControllerClass)) {
176 | continue;
177 | }
178 |
179 | NSString *className = NSStringFromClass(aClass);
180 | if ([ignoredClasses containsObject:className]) {
181 | continue;
182 | }
183 | BOOL hasIgnoredPrefix = NO;
184 | for (NSString *prefix in ignoredPrefixes) {
185 | if ([className hasPrefix:prefix]) {
186 | hasIgnoredPrefix = YES;
187 | break;
188 | }
189 | }
190 | if (hasIgnoredPrefix) {
191 | continue;
192 | }
193 |
194 | [classes addObject:aClass];
195 | }
196 |
197 | free(classList);
198 |
199 | return classes;
200 | }
201 |
202 | NSArray *CBCClassesRespondingToSelector(NSArray *classes, SEL selector) {
203 | NSMutableArray *filteredClasses = [NSMutableArray array];
204 | for (Class aClass in classes) {
205 | if ([aClass respondsToSelector:selector]) {
206 | [filteredClasses addObject:aClass];
207 | }
208 | }
209 | return filteredClasses;
210 | }
211 |
212 | #pragma mark UIViewController instantiation
213 |
214 | UIViewController *CBCViewControllerFromClass(Class aClass, NSDictionary *metadata) {
215 | if ([metadata objectForKey:CBCStoryboardName]) {
216 | NSString *storyboardName = [metadata objectForKey:CBCStoryboardName];
217 | NSBundle *bundle = [NSBundle bundleForClass:aClass];
218 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle];
219 | NSCAssert(storyboard, @"expecting a storyboard to exist at %@", storyboardName);
220 | UIViewController *vc = [storyboard instantiateInitialViewController];
221 | NSCAssert(vc, @"expecting a initialViewController in the storyboard %@", storyboardName);
222 | return vc;
223 | }
224 | return [[aClass alloc] init];
225 | }
226 |
227 | #pragma mark Fix View Debugging
228 |
229 | void CBCFixViewDebuggingIfNeeded(void) {
230 | static dispatch_once_t onceToken;
231 | dispatch_once(&onceToken, ^{
232 | Method original = class_getInstanceMethod([UIView class], @selector(viewForBaselineLayout));
233 | class_addMethod([UIView class], @selector(viewForFirstBaselineLayout),
234 | method_getImplementation(original), method_getTypeEncoding(original));
235 | class_addMethod([UIView class], @selector(viewForLastBaselineLayout),
236 | method_getImplementation(original), method_getTypeEncoding(original));
237 | });
238 | }
239 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 2.5.2
2 |
3 | This patch release includes performance improvements and support for macCatalyst apps.
4 |
5 | # 2.5.1
6 |
7 | Fix a runtime crash that occurred when the PhotoFoundation framework was included in an app.
8 |
9 | ## Source changes
10 |
11 | * [Ignore PhotoFoundation classes in the runtime lookup (#31)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/2e8866dcd15ed6a3e755c054510333438802e0a6) (featherless)
12 |
13 | # 2.5.0
14 |
15 | There is now a new `NSDictionary` property in `CBCNode` called metadata. It is meant to store all the information regarding an example
16 | rather than using separate methods as previously done. With that said, we offer backwards compatibility and still allow the usage of methods to provide example information.
17 |
18 | Before:
19 | ```
20 | + (NSArray *)catalogBreadcrumbs {
21 | return @[ @"Activity Indicator", @"Activity Indicator" ];
22 | }
23 |
24 | + (NSString *)catalogDescription {
25 | return @"Activity Indicator is a visual indication of an app loading content. It can display how "
26 | @"long an operation will take or visualize an unspecified wait time.";
27 | }
28 |
29 | + (BOOL)catalogIsPrimaryDemo {
30 | return YES;
31 | }
32 |
33 | + (BOOL)catalogIsPresentable {
34 | return YES;
35 | }
36 | ```
37 |
38 | After:
39 | ```
40 | + (NSDictionary *)catalogMetadata {
41 | return @{@"breadcrumbs": @[ @"Activity Indicator", @"Activity Indicator" ],
42 | @"description": @"Activity Indicator is a visual indication of an app loading content. It can display how "
43 | @"long an operation will take or visualize an unspecified wait time.",
44 | @"primaryDemo": @YES,
45 | @"presentable": @YES};
46 | }
47 | ```
48 |
49 | ## Source changes
50 |
51 | * [added a new metadata property that will hold all the key/values for that node. Also code refactoring (#27)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/6c44e443e98bb87c955663c45c1245921338de1e) (Yarden Eitan)
52 |
53 | ## API changes
54 |
55 | #### CBCBreadcrumbs
56 |
57 | *new* constant: `CBCBreadcrumbs`
58 |
59 | #### CBCNode
60 |
61 | *new* property: `metadata` in `CBCNode`
62 |
63 | *removed* property: `nodeDescription` in `CBCNode`
64 |
65 | *modified* method: `-exampleDescription` in `CBCNode`
66 |
67 | | Type of change: | Swift declaration |
68 | |---|---|
69 | | From: | `func exampleDescription() -> String` |
70 | | To: | `func exampleDescription() -> String?` |
71 |
72 | *modified* method: `-exampleDescription` in `CBCNode`
73 |
74 | | Type of change: | Declaration |
75 | |---|---|
76 | | From: | `- (nonnull NSString *)exampleDescription;` |
77 | | To: | `- (nullable NSString *)exampleDescription;` |
78 |
79 | #### CBCRelatedInfo
80 |
81 | *new* constant: `CBCRelatedInfo`
82 |
83 | #### CBCIsDebug
84 |
85 | *new* constant: `CBCIsDebug`
86 |
87 | #### CBCIsPresentable
88 |
89 | *new* constant: `CBCIsPresentable`
90 |
91 | #### CBCIsPrimaryDemo
92 |
93 | *new* constant: `CBCIsPrimaryDemo`
94 |
95 | #### CBCDescription
96 |
97 | *new* constant: `CBCDescription`
98 |
99 | #### CBCStoryboardName
100 |
101 | *new* constant: `CBCStoryboardName`
102 |
103 | # 2.4.1
104 |
105 | Add `exampleRelatedInfo` to the CBCNode header for external invocation.
106 |
107 | ## Source changes
108 |
109 | * [add exampleRelatedInfo to header file (#26)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/57edd7f16ea7ec40d62378faacbc95071310bfbc) (Yarden Eitan)
110 |
111 | # 2.4.0
112 |
113 | - Now you can add the method `catalogRelatedInfo` that returns an NSURL to your example, if you wish to link to related information and resources.
114 | - A performance improvement when fetching all viable classes to build the navigation tree.
115 |
116 | ## Source changes
117 |
118 | * [Add "related info" URLs to examples (#24)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/ee57bf7bb544b105c5d91aaa2ef348d0f663a690) (Adrian Secord)
119 | * [[Runtime] Only select UIViewController subclasses (#22)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/ce864aabf505978a3933a93bfcf048f5d41bc071) (Robert Moore)
120 |
121 | # 2.3.1
122 |
123 | minor bug fix introduced in 2.3.0 that returns wrong boolean values.
124 |
125 | ## Source changes
126 |
127 | * [PR fixes introduced a minor bug, need to update release (#20)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/6be4d710a05dbe981728af00700e20268ca548a9) (Yarden Eitan)
128 |
129 | # 2.3.0
130 |
131 | This minor release adds two new functionalities that support our new Dragons app.
132 |
133 | ## New features
134 |
135 | It's now possible to call `CBCCreatePresentableNavigationTree` to create a navigation tree that only consists of examples that implement the `catalogIsPresentable` method and return `YES` to it.
136 | `CBCNode` now has a new property called `debugLeaf` which is also a `CBCNode`. If an example implements the `catalogIsDebug` method and returns `YES`, then that example will become the
137 | `debugLeaf`. When the `debugLeaf` is set, then in the Dragons app it will become the initial view controller in the navigation. NOTE: If there are multiple examples that return `YES` to
138 | `catalogIsDebug` then the last class that is parsed while going through the hierarchy is the one to be set as the `debugLeaf`.
139 |
140 | ## Source changes
141 |
142 | * [Additional functionality to CbC to support Dragons (#19)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/67c6d97e80c465d5915dc6dff4c6c19f53627bb8) (Yarden Eitan)
143 |
144 | ## Non-source changes
145 |
146 | * [Remove arc support. (#18)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/1fcaf777143b7906958b1cccc3a861320c45ce36) (featherless)
147 | * [Bump the kokoro runner version to v2.1.1.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/a49bf18bc839f86879473329a85f7939e0b115c8) (Jeff Verkoeyen)
148 | * [Replace Carthage support with bazel support (#17)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/dc45a1a6ef5ad92325ee2799b76920375141dacc) (featherless)
149 |
150 | # 2.2.0
151 |
152 | This minor release introduces support for Carthage.
153 |
154 | ## New features
155 |
156 | It's now possible to define multiple paths to a single example. Simply return an array of arrays
157 | of breadcrumbs from the `catalogBreadcrumbs` implementation.
158 |
159 | ## Source changes
160 |
161 | * [added ability to have multiple parallel bread crumbs to get to a view controller.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/cc6a0b16dc41cc044d2ca0a98aa2dcbd35a7c2c5) (randallli)
162 |
163 | ## Non-source changes
164 |
165 | * [Disable code coverage reporting. (#16)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/098188b6353e96f1ffebe2749816e859ba1e8d72) (featherless)
166 | * [Add kokoro continuous build script. (#15)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/ac9cc4b1c67b74c2c03c1d12c1905dfb47a0a141) (featherless)
167 | * [Fix the xcodeproj. (#14)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/a25b7b00664903e90e9be058e5e7826213b6295b) (featherless)
168 | * [Add support for Carthage.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/30dfc96ae85c5e32040304ba584ad6663c9a931f) (Jeff Verkoeyen)
169 | * [fixed method signature](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/9cc0050858eb26dd6bd0c0ecef1f6ffcca6a49e1) (randallli)
170 | * [Add .swift-version file.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/3e38db52bd3d245ade4734394295894e123b1e59) (Jeff Verkoeyen)
171 |
172 | # 2.1.1
173 |
174 | - Fixed a crashing bug on iOS 10.3.1.
175 |
176 | # 2.1.0
177 |
178 | - Add tvOS as a platform.
179 |
180 | # 2.0.1
181 |
182 | - Fixed some warnings.
183 |
184 | ## Source changes
185 |
186 | * [Merge pull request #6 from randallli/fixWarnings](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/8136bf10acab15ebfb12de080e919f6540753dd9) (Randall Li)
187 |
188 | # 2.0.0
189 |
190 | - Upgraded to Swift 3.
191 | - Resolved nullability warning in CBCNodeListViewController.
192 |
193 | ## Source changes
194 |
195 | * [Resolve nullability warning in CBCNodeListViewController.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/aba9ba241b0c93b23aeff2dffbf840308fa1c6a9) (Jeff Verkoeyen)
196 |
197 | ## Non-source changes
198 |
199 | * [Update CHANGELOG.md.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/8749999cea843119c585267211bcebbdd482a5bf) (Jeff Verkoeyen)
200 | * [Automatic changelog preparation for release.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/204bcbf77edae27053e60f9e6c21d36bfb8d48c2) (Jeff Verkoeyen)
201 | * [Upgrade project to Xcode 8 and Swift 3.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/6d7d7e6786ccafe9a267641c48a71859710c5cc0) (Jeff Verkoeyen)
202 | * [Update Podfile.lock.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/d273761f8c452b2cf7fc1f97141320a8f5978ff4) (Jeff Verkoeyen)
203 | * [Add explanation for adding new examples.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/452e5f715adb1c5a1d68f4d642d20ce9ba51b875) (Jeff Verkoeyen)
204 | * [Add notes regarding creating a unit test target and ensuring names match.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/98639ec477c050a8a3d5bb14137fb70a2064bc0f) (Jeff Verkoeyen)
205 | * [Replace tabs with spaces.](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/a26020e6bc55f2a4a19eb45bd218109eea2ddcd1) (Jeff Verkoeyen)
206 |
207 | # 1.0.1
208 |
209 | - Add missing header import to src/CBCCatalogExample.h.
210 | - Add exampleViewControllerName API to CBCNode.
211 | - Use modern nullability annotations.
212 | - API docs now available at https://material-foundation.github.io/cocoapods-catalog-by-convention/.
213 |
214 | # 1.0.0
215 |
216 | - Initial release.
217 |
218 | Includes support for examples and unit tests by convention.
219 |
220 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/example/catalog/UnitTests.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3FAAAB8760AC01023F399C0E /* Pods_Catalog_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C240522789AEE2237903DA6 /* Pods_Catalog_UnitTests.framework */; };
11 | 666CA7051CAE3628001B1884 /* XcodeCrashFix7.2.1.m in Sources */ = {isa = PBXBuildFile; fileRef = 666CA7031CAE3532001B1884 /* XcodeCrashFix7.2.1.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXFileReference section */
15 | 3390CD8F587A4A3CF563BB0E /* Pods-Catalog-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Catalog-UnitTests.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-Catalog-UnitTests/Pods-Catalog-UnitTests.debug.xcconfig"; sourceTree = ""; };
16 | 3D464296ABDD2726AA4FDF9F /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = ""; };
17 | 3FBF65A91EB5B22C45EA3C9B /* Pods_UnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
18 | 52F8F8BECC5B67A3F5BA0E20 /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = ""; };
19 | 666CA6F81CAE34F0001B1884 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
20 | 666CA6FC1CAE34F0001B1884 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../UnitTests/Info.plist; sourceTree = ""; };
21 | 666CA7031CAE3532001B1884 /* XcodeCrashFix7.2.1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XcodeCrashFix7.2.1.m; sourceTree = ""; };
22 | 9C240522789AEE2237903DA6 /* Pods_Catalog_UnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Catalog_UnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
23 | C96DDDFCA1936FA012354A56 /* Pods-Catalog-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Catalog-UnitTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-Catalog-UnitTests/Pods-Catalog-UnitTests.release.xcconfig"; sourceTree = ""; };
24 | /* End PBXFileReference section */
25 |
26 | /* Begin PBXFrameworksBuildPhase section */
27 | 666CA6F51CAE34F0001B1884 /* Frameworks */ = {
28 | isa = PBXFrameworksBuildPhase;
29 | buildActionMask = 2147483647;
30 | files = (
31 | 3FAAAB8760AC01023F399C0E /* Pods_Catalog_UnitTests.framework in Frameworks */,
32 | );
33 | runOnlyForDeploymentPostprocessing = 0;
34 | };
35 | /* End PBXFrameworksBuildPhase section */
36 |
37 | /* Begin PBXGroup section */
38 | 0B1714C5C78BD56BEB4A23A4 /* Frameworks */ = {
39 | isa = PBXGroup;
40 | children = (
41 | 3FBF65A91EB5B22C45EA3C9B /* Pods_UnitTests.framework */,
42 | 9C240522789AEE2237903DA6 /* Pods_Catalog_UnitTests.framework */,
43 | );
44 | name = Frameworks;
45 | sourceTree = "";
46 | };
47 | 666CA6DF1CAE34E1001B1884 = {
48 | isa = PBXGroup;
49 | children = (
50 | 666CA6EA1CAE34E1001B1884 /* UnitTests */,
51 | 666CA6E91CAE34E1001B1884 /* Products */,
52 | 83252B2C0D7147765E8C58A1 /* Pods */,
53 | 0B1714C5C78BD56BEB4A23A4 /* Frameworks */,
54 | );
55 | sourceTree = "";
56 | };
57 | 666CA6E91CAE34E1001B1884 /* Products */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 666CA6F81CAE34F0001B1884 /* UnitTests.xctest */,
61 | );
62 | name = Products;
63 | sourceTree = "";
64 | };
65 | 666CA6EA1CAE34E1001B1884 /* UnitTests */ = {
66 | isa = PBXGroup;
67 | children = (
68 | 666CA7031CAE3532001B1884 /* XcodeCrashFix7.2.1.m */,
69 | 666CA6FC1CAE34F0001B1884 /* Info.plist */,
70 | );
71 | path = UnitTests;
72 | sourceTree = "";
73 | };
74 | 83252B2C0D7147765E8C58A1 /* Pods */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 3D464296ABDD2726AA4FDF9F /* Pods-UnitTests.debug.xcconfig */,
78 | 52F8F8BECC5B67A3F5BA0E20 /* Pods-UnitTests.release.xcconfig */,
79 | 3390CD8F587A4A3CF563BB0E /* Pods-Catalog-UnitTests.debug.xcconfig */,
80 | C96DDDFCA1936FA012354A56 /* Pods-Catalog-UnitTests.release.xcconfig */,
81 | );
82 | name = Pods;
83 | sourceTree = "";
84 | };
85 | /* End PBXGroup section */
86 |
87 | /* Begin PBXNativeTarget section */
88 | 666CA6F71CAE34F0001B1884 /* UnitTests */ = {
89 | isa = PBXNativeTarget;
90 | buildConfigurationList = 666CA7001CAE34F0001B1884 /* Build configuration list for PBXNativeTarget "UnitTests" */;
91 | buildPhases = (
92 | 9CA275C50F46028F0EBA13E7 /* [CP] Check Pods Manifest.lock */,
93 | 666CA6F41CAE34F0001B1884 /* Sources */,
94 | 666CA6F51CAE34F0001B1884 /* Frameworks */,
95 | 666CA6F61CAE34F0001B1884 /* Resources */,
96 | 8F5625CD97931E20C8116BFF /* [CP] Embed Pods Frameworks */,
97 | E2A6B4BD8988E7DF3FD232F4 /* [CP] Copy Pods Resources */,
98 | );
99 | buildRules = (
100 | );
101 | dependencies = (
102 | );
103 | name = UnitTests;
104 | productName = UnitTests;
105 | productReference = 666CA6F81CAE34F0001B1884 /* UnitTests.xctest */;
106 | productType = "com.apple.product-type.bundle.unit-test";
107 | };
108 | /* End PBXNativeTarget section */
109 |
110 | /* Begin PBXProject section */
111 | 666CA6E01CAE34E1001B1884 /* Project object */ = {
112 | isa = PBXProject;
113 | attributes = {
114 | LastUpgradeCheck = 0800;
115 | ORGANIZATIONNAME = Google;
116 | TargetAttributes = {
117 | 666CA6F71CAE34F0001B1884 = {
118 | CreatedOnToolsVersion = 7.2.1;
119 | LastSwiftMigration = 0800;
120 | };
121 | };
122 | };
123 | buildConfigurationList = 666CA6E31CAE34E1001B1884 /* Build configuration list for PBXProject "UnitTests" */;
124 | compatibilityVersion = "Xcode 3.2";
125 | developmentRegion = English;
126 | hasScannedForEncodings = 0;
127 | knownRegions = (
128 | en,
129 | );
130 | mainGroup = 666CA6DF1CAE34E1001B1884;
131 | productRefGroup = 666CA6E91CAE34E1001B1884 /* Products */;
132 | projectDirPath = "";
133 | projectRoot = "";
134 | targets = (
135 | 666CA6F71CAE34F0001B1884 /* UnitTests */,
136 | );
137 | };
138 | /* End PBXProject section */
139 |
140 | /* Begin PBXResourcesBuildPhase section */
141 | 666CA6F61CAE34F0001B1884 /* Resources */ = {
142 | isa = PBXResourcesBuildPhase;
143 | buildActionMask = 2147483647;
144 | files = (
145 | );
146 | runOnlyForDeploymentPostprocessing = 0;
147 | };
148 | /* End PBXResourcesBuildPhase section */
149 |
150 | /* Begin PBXShellScriptBuildPhase section */
151 | 8F5625CD97931E20C8116BFF /* [CP] Embed Pods Frameworks */ = {
152 | isa = PBXShellScriptBuildPhase;
153 | buildActionMask = 2147483647;
154 | files = (
155 | );
156 | inputPaths = (
157 | "${SRCROOT}/../Pods/Target Support Files/Pods-Catalog-UnitTests/Pods-Catalog-UnitTests-frameworks.sh",
158 | "${BUILT_PRODUCTS_DIR}/CatalogByConvention/CatalogByConvention.framework",
159 | "${BUILT_PRODUCTS_DIR}/Resistor/Resistor.framework",
160 | "${BUILT_PRODUCTS_DIR}/CatalogUnitTests/CatalogUnitTests.framework",
161 | );
162 | name = "[CP] Embed Pods Frameworks";
163 | outputPaths = (
164 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CatalogByConvention.framework",
165 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Resistor.framework",
166 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CatalogUnitTests.framework",
167 | );
168 | runOnlyForDeploymentPostprocessing = 0;
169 | shellPath = /bin/sh;
170 | shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-Catalog-UnitTests/Pods-Catalog-UnitTests-frameworks.sh\"\n";
171 | showEnvVarsInLog = 0;
172 | };
173 | 9CA275C50F46028F0EBA13E7 /* [CP] Check Pods Manifest.lock */ = {
174 | isa = PBXShellScriptBuildPhase;
175 | buildActionMask = 2147483647;
176 | files = (
177 | );
178 | inputPaths = (
179 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
180 | "${PODS_ROOT}/Manifest.lock",
181 | );
182 | name = "[CP] Check Pods Manifest.lock";
183 | outputPaths = (
184 | "$(DERIVED_FILE_DIR)/Pods-Catalog-UnitTests-checkManifestLockResult.txt",
185 | );
186 | runOnlyForDeploymentPostprocessing = 0;
187 | shellPath = /bin/sh;
188 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
189 | showEnvVarsInLog = 0;
190 | };
191 | E2A6B4BD8988E7DF3FD232F4 /* [CP] Copy Pods Resources */ = {
192 | isa = PBXShellScriptBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | );
196 | inputPaths = (
197 | );
198 | name = "[CP] Copy Pods Resources";
199 | outputPaths = (
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | shellPath = /bin/sh;
203 | shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-Catalog-UnitTests/Pods-Catalog-UnitTests-resources.sh\"\n";
204 | showEnvVarsInLog = 0;
205 | };
206 | /* End PBXShellScriptBuildPhase section */
207 |
208 | /* Begin PBXSourcesBuildPhase section */
209 | 666CA6F41CAE34F0001B1884 /* Sources */ = {
210 | isa = PBXSourcesBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | 666CA7051CAE3628001B1884 /* XcodeCrashFix7.2.1.m in Sources */,
214 | );
215 | runOnlyForDeploymentPostprocessing = 0;
216 | };
217 | /* End PBXSourcesBuildPhase section */
218 |
219 | /* Begin XCBuildConfiguration section */
220 | 666CA6EF1CAE34E1001B1884 /* Debug */ = {
221 | isa = XCBuildConfiguration;
222 | buildSettings = {
223 | ALWAYS_SEARCH_USER_PATHS = NO;
224 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
225 | CLANG_CXX_LIBRARY = "libc++";
226 | CLANG_ENABLE_MODULES = YES;
227 | CLANG_ENABLE_OBJC_ARC = YES;
228 | CLANG_WARN_BOOL_CONVERSION = YES;
229 | CLANG_WARN_CONSTANT_CONVERSION = YES;
230 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
231 | CLANG_WARN_EMPTY_BODY = YES;
232 | CLANG_WARN_ENUM_CONVERSION = YES;
233 | CLANG_WARN_INFINITE_RECURSION = YES;
234 | CLANG_WARN_INT_CONVERSION = YES;
235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
236 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
237 | CLANG_WARN_UNREACHABLE_CODE = YES;
238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
239 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
240 | COPY_PHASE_STRIP = NO;
241 | DEBUG_INFORMATION_FORMAT = dwarf;
242 | ENABLE_STRICT_OBJC_MSGSEND = YES;
243 | ENABLE_TESTABILITY = YES;
244 | GCC_C_LANGUAGE_STANDARD = gnu99;
245 | GCC_DYNAMIC_NO_PIC = NO;
246 | GCC_NO_COMMON_BLOCKS = YES;
247 | GCC_OPTIMIZATION_LEVEL = 0;
248 | GCC_PREPROCESSOR_DEFINITIONS = (
249 | "DEBUG=1",
250 | "$(inherited)",
251 | );
252 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
253 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
254 | GCC_WARN_UNDECLARED_SELECTOR = YES;
255 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
256 | GCC_WARN_UNUSED_FUNCTION = YES;
257 | GCC_WARN_UNUSED_VARIABLE = YES;
258 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
259 | MTL_ENABLE_DEBUG_INFO = YES;
260 | ONLY_ACTIVE_ARCH = YES;
261 | SDKROOT = iphoneos;
262 | };
263 | name = Debug;
264 | };
265 | 666CA6F01CAE34E1001B1884 /* Release */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | ALWAYS_SEARCH_USER_PATHS = NO;
269 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
270 | CLANG_CXX_LIBRARY = "libc++";
271 | CLANG_ENABLE_MODULES = YES;
272 | CLANG_ENABLE_OBJC_ARC = YES;
273 | CLANG_WARN_BOOL_CONVERSION = YES;
274 | CLANG_WARN_CONSTANT_CONVERSION = YES;
275 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
276 | CLANG_WARN_EMPTY_BODY = YES;
277 | CLANG_WARN_ENUM_CONVERSION = YES;
278 | CLANG_WARN_INFINITE_RECURSION = YES;
279 | CLANG_WARN_INT_CONVERSION = YES;
280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
281 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
282 | CLANG_WARN_UNREACHABLE_CODE = YES;
283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
284 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
285 | COPY_PHASE_STRIP = NO;
286 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
287 | ENABLE_NS_ASSERTIONS = NO;
288 | ENABLE_STRICT_OBJC_MSGSEND = YES;
289 | GCC_C_LANGUAGE_STANDARD = gnu99;
290 | GCC_NO_COMMON_BLOCKS = YES;
291 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
292 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
293 | GCC_WARN_UNDECLARED_SELECTOR = YES;
294 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
295 | GCC_WARN_UNUSED_FUNCTION = YES;
296 | GCC_WARN_UNUSED_VARIABLE = YES;
297 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
298 | MTL_ENABLE_DEBUG_INFO = NO;
299 | SDKROOT = iphoneos;
300 | VALIDATE_PRODUCT = YES;
301 | };
302 | name = Release;
303 | };
304 | 666CA7011CAE34F0001B1884 /* Debug */ = {
305 | isa = XCBuildConfiguration;
306 | baseConfigurationReference = 3390CD8F587A4A3CF563BB0E /* Pods-Catalog-UnitTests.debug.xcconfig */;
307 | buildSettings = {
308 | INFOPLIST_FILE = UnitTests/Info.plist;
309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
310 | PRODUCT_BUNDLE_IDENTIFIER = com.google.UnitTests;
311 | PRODUCT_NAME = "$(TARGET_NAME)";
312 | SWIFT_VERSION = 3.0;
313 | };
314 | name = Debug;
315 | };
316 | 666CA7021CAE34F0001B1884 /* Release */ = {
317 | isa = XCBuildConfiguration;
318 | baseConfigurationReference = C96DDDFCA1936FA012354A56 /* Pods-Catalog-UnitTests.release.xcconfig */;
319 | buildSettings = {
320 | INFOPLIST_FILE = UnitTests/Info.plist;
321 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
322 | PRODUCT_BUNDLE_IDENTIFIER = com.google.UnitTests;
323 | PRODUCT_NAME = "$(TARGET_NAME)";
324 | SWIFT_VERSION = 3.0;
325 | };
326 | name = Release;
327 | };
328 | /* End XCBuildConfiguration section */
329 |
330 | /* Begin XCConfigurationList section */
331 | 666CA6E31CAE34E1001B1884 /* Build configuration list for PBXProject "UnitTests" */ = {
332 | isa = XCConfigurationList;
333 | buildConfigurations = (
334 | 666CA6EF1CAE34E1001B1884 /* Debug */,
335 | 666CA6F01CAE34E1001B1884 /* Release */,
336 | );
337 | defaultConfigurationIsVisible = 0;
338 | defaultConfigurationName = Release;
339 | };
340 | 666CA7001CAE34F0001B1884 /* Build configuration list for PBXNativeTarget "UnitTests" */ = {
341 | isa = XCConfigurationList;
342 | buildConfigurations = (
343 | 666CA7011CAE34F0001B1884 /* Debug */,
344 | 666CA7021CAE34F0001B1884 /* Release */,
345 | );
346 | defaultConfigurationIsVisible = 0;
347 | defaultConfigurationName = Release;
348 | };
349 | /* End XCConfigurationList section */
350 | };
351 | rootObject = 666CA6E01CAE34E1001B1884 /* Project object */;
352 | }
353 |
--------------------------------------------------------------------------------
/src/CBCNodeListViewController.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016-present Google Inc. All Rights Reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | #import "CBCNodeListViewController.h"
18 |
19 | #import "private/CBCRuntime.h"
20 |
21 | @interface CBCNode()
22 | @property(nonatomic, strong, nullable) NSMutableDictionary *map;
23 | @property(nonatomic, strong, nullable) Class exampleClass;
24 | @property(nonatomic, copy, nullable) NSSet *keyWords;
25 | @end
26 |
27 | @implementation CBCNode {
28 | NSMutableArray *_children;
29 | }
30 |
31 | - (instancetype)initWithTitle:(NSString *)title {
32 | self = [super init];
33 | if (self) {
34 | _title = [title copy];
35 | self.map = [NSMutableDictionary dictionary];
36 | _children = [NSMutableArray array];
37 | CBCFixViewDebuggingIfNeeded();
38 | }
39 | return self;
40 | }
41 |
42 | - (NSComparisonResult)compare:(CBCNode *)otherObject {
43 | return [self.title compare:otherObject.title];
44 | }
45 |
46 | - (void)addChild:(CBCNode *)child {
47 | self.map[child.title] = child;
48 | [_children addObject:child];
49 | }
50 |
51 | - (void)finalizeNode {
52 | _children = [[_children sortedArrayUsingSelector:@selector(compare:)] mutableCopy];
53 | }
54 |
55 | #pragma mark Public
56 |
57 | - (BOOL)isExample {
58 | return self.exampleClass != nil;
59 | }
60 |
61 | - (NSString *)exampleViewControllerName {
62 | NSAssert(self.exampleClass != nil, @"This node has no associated example.");
63 | return NSStringFromClass(_exampleClass);
64 | }
65 |
66 | - (UIViewController *)createExampleViewController {
67 | NSAssert(self.exampleClass != nil, @"This node has no associated example.");
68 | return CBCViewControllerFromClass(self.exampleClass, self.metadata);
69 | }
70 |
71 | - (NSString *)exampleDescription {
72 | NSString *description = [self.metadata objectForKey:CBCDescription];
73 | if (description != nil && [description isKindOfClass:[NSString class]]) {
74 | return description;
75 | }
76 | return nil;
77 | }
78 |
79 | - (NSURL *)exampleRelatedInfo {
80 | NSURL *relatedInfo = [self.metadata objectForKey:CBCRelatedInfo];
81 | if (relatedInfo != nil && [relatedInfo isKindOfClass:[NSURL class]]) {
82 | return relatedInfo;
83 | }
84 | return nil;
85 | }
86 |
87 | - (BOOL)isPrimaryDemo {
88 | id isPrimaryDemo;
89 | if ((isPrimaryDemo = [self.metadata objectForKey:CBCIsPrimaryDemo]) != nil) {
90 | return [isPrimaryDemo boolValue];
91 | }
92 | return NO;
93 | }
94 |
95 | - (BOOL)isPresentable {
96 | id isPresentable;
97 | if ((isPresentable = [self.metadata objectForKey:CBCIsPresentable]) != nil) {
98 | return [isPresentable boolValue];
99 | }
100 | return NO;
101 | }
102 |
103 | @end
104 |
105 | static NSArray *CBCGetSearchResultsFromNode(CBCNode *node, NSSet *filters, NSString *searchText) {
106 | NSMutableArray *searchResults = [NSMutableArray array];
107 |
108 | // Breadth-first search through the navigation tree starting at the given node.
109 | NSMutableArray *queue = [NSMutableArray arrayWithObject:node];
110 | while ([queue count] > 0) {
111 | CBCNode *current = [queue firstObject];
112 | [queue removeObjectAtIndex:0];
113 | if (current.children.count > 0) {
114 | [queue addObjectsFromArray:current.children];
115 | continue;
116 | }
117 |
118 | // If the node is an example, conduct the filtering.
119 | if (filters.count > 0) {
120 | NSMutableSet *keywordKeys = [current.keyWords mutableCopy];
121 | [keywordKeys intersectSet:filters];
122 | // If the keywords do not contain all of the filters, do not add the node.
123 | if (keywordKeys.count != filters.count) {
124 | continue;
125 | }
126 | }
127 | if (searchText.length > 0) {
128 | for (NSString *keyword in current.keyWords) {
129 | if ([keyword containsString:searchText.lowercaseString]) {
130 | [searchResults addObject:current];
131 | break;
132 | }
133 | }
134 | } else {
135 | // Add the node if there is no search text and it passes the filter check.
136 | [searchResults addObject:current];
137 | }
138 | }
139 | return searchResults;
140 | }
141 |
142 | static NSDictionary *> *CBCGetSearchResultGroupedNodes(
143 | NSDictionary *> *groupedNodes, NSSet *filters, NSString *searchText) {
144 | NSMutableDictionary *> *searchResultGroupedNodes =
145 | [NSMutableDictionary dictionary];
146 | for (NSString *group in groupedNodes) {
147 | NSMutableArray *searchResults = [NSMutableArray array];
148 | for (CBCNode *node in groupedNodes[group]) {
149 | [searchResults addObjectsFromArray:CBCGetSearchResultsFromNode(node, filters, searchText)];
150 | }
151 | if (searchResults.count > 0) {
152 | searchResultGroupedNodes[group] = searchResults;
153 | }
154 | }
155 | return searchResultGroupedNodes;
156 | }
157 |
158 | @interface CBCNodeListViewController ()
159 | @property(nonatomic) NSArray *groups;
160 | @property(nonatomic) NSDictionary *> *groupedNodes;
161 | @property(nonatomic) NSDictionary *> *searchResultGroupedNodes;
162 | @property(nonatomic) NSMutableSet *filters;
163 | @property(nonatomic, copy, nullable) NSString *searchText;
164 | @end
165 |
166 | @implementation CBCNodeListViewController
167 |
168 | - (instancetype)initWithNode:(CBCNode *)node {
169 | NSAssert(!self.node.isExample, @"%@ cannot represent example nodes.",
170 | NSStringFromClass([self class]));
171 |
172 | self = [super initWithNibName:nil bundle:nil];
173 | if (self) {
174 | _node = node;
175 |
176 | NSMutableSet *groups = [NSMutableSet set];
177 | NSMutableDictionary *> *groupedNodes =
178 | [NSMutableDictionary dictionary];
179 | for (CBCNode *child in _node.children) {
180 | NSString *group = child.group;
181 | if (group == nil) {
182 | group = @""; // Ungrouped items get placed in a default group.
183 | }
184 | // Convert to lowercase to ensure case-insensitive grouping since group titles will always be
185 | // capitalized in the UI.
186 | [groups addObject:group];
187 |
188 | NSMutableArray *nodes = groupedNodes[group];
189 | if (nodes == nil) {
190 | nodes = [NSMutableArray array];
191 | groupedNodes[group] = nodes;
192 | }
193 | [nodes addObject:child];
194 | }
195 | _groups = [groups sortedArrayUsingDescriptors:@[
196 | [NSSortDescriptor sortDescriptorWithKey:@"self"
197 | ascending:YES
198 | selector:@selector(localizedCaseInsensitiveCompare:)]
199 | ]];
200 | _groupedNodes = groupedNodes;
201 | _filters = [NSMutableSet set];
202 | _searchEnabled = NO;
203 | _searchResultGroupedNodes = [NSMutableDictionary dictionary];
204 |
205 | self.title = self.node.title;
206 | }
207 | return self;
208 | }
209 |
210 | - (void)viewDidLoad {
211 | [super viewDidLoad];
212 |
213 | UITableViewStyle style = UITableViewStyleGrouped;
214 | #if !TARGET_OS_TV
215 | style = UITableViewStyleInsetGrouped;
216 | if (@available(iOS 13.0, *)) {
217 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
218 | style = UITableViewStyleInsetGrouped;
219 | }
220 | }
221 | #endif
222 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:style];
223 | self.tableView.autoresizingMask =
224 | (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
225 | self.tableView.delegate = self;
226 | self.tableView.dataSource = self;
227 | [self.view addSubview:self.tableView];
228 | }
229 |
230 | - (void)viewWillAppear:(BOOL)animated {
231 | [super viewWillAppear:animated];
232 |
233 | NSIndexPath *selectedRow = self.tableView.indexPathForSelectedRow;
234 | if (selectedRow) {
235 | [[self transitionCoordinator] animateAlongsideTransition:^(id context) {
236 | [self.tableView deselectRowAtIndexPath:selectedRow animated:YES];
237 | }
238 | completion:^(id context) {
239 | if ([context isCancelled]) {
240 | [self.tableView selectRowAtIndexPath:selectedRow
241 | animated:NO
242 | scrollPosition:UITableViewScrollPositionNone];
243 | }
244 | }];
245 | }
246 | }
247 |
248 | - (void)viewDidAppear:(BOOL)animated {
249 | [super viewDidAppear:animated];
250 |
251 | [self.tableView flashScrollIndicators];
252 | }
253 |
254 | #pragma mark - UITableViewDataSource
255 |
256 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
257 | if (_searchEnabled) {
258 | return [_searchResultGroupedNodes count];
259 | }
260 | return [_groups count];
261 | }
262 |
263 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
264 | if (_searchEnabled) {
265 | return [_searchResultGroupedNodes.allKeys objectAtIndex:section];
266 | }
267 | return _groups[section];
268 | }
269 |
270 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
271 | NSString *group = _groups[section];
272 | if (_searchEnabled) {
273 | group = [_searchResultGroupedNodes.allKeys objectAtIndex:section];
274 | return (NSInteger)[_searchResultGroupedNodes[group] count];
275 | }
276 | return (NSInteger)[self.groupedNodes[group] count];
277 | }
278 |
279 | - (UITableViewCell *)tableView:(UITableView *)tableView
280 | cellForRowAtIndexPath:(NSIndexPath *)indexPath {
281 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
282 | if (!cell) {
283 | cell =
284 | [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
285 | }
286 | CBCNode *node = [self nodeForIndexPath:indexPath];
287 | cell.textLabel.text = node.title;
288 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
289 | return cell;
290 | }
291 |
292 | #pragma mark - UITableViewDelegate
293 |
294 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
295 | CBCNode *node = [self nodeForIndexPath:indexPath];
296 | UIViewController *viewController = nil;
297 | if ([node isExample]) {
298 | viewController = [node createExampleViewController];
299 | } else {
300 | viewController = [[[self class] alloc] initWithNode:node];
301 | }
302 | [self.navigationController pushViewController:viewController animated:YES];
303 | }
304 |
305 | #pragma mark - Model
306 |
307 | - (CBCNode *)nodeForIndexPath:(NSIndexPath *)indexPath {
308 | NSString *group = _groups[indexPath.section];
309 | if (_searchEnabled) {
310 | group = [_searchResultGroupedNodes.allKeys objectAtIndex:indexPath.section];
311 | return [_searchResultGroupedNodes[group] objectAtIndex:indexPath.row];
312 | }
313 | return self.groupedNodes[group][(NSUInteger)indexPath.row];
314 | }
315 |
316 | #pragma mark - Filter and Search
317 |
318 | - (void)updateFilters:(NSString *)filter enabled:(BOOL)enabled {
319 | if (enabled) {
320 | [self.filters addObject:filter.lowercaseString];
321 | } else {
322 | [self.filters removeObject:filter.lowercaseString];
323 | }
324 | [self search];
325 | }
326 |
327 | - (void)findSearchText:(NSString *)searchText {
328 | self.searchText = searchText;
329 | [self search];
330 | }
331 |
332 | - (void)search {
333 | self.searchEnabled = self.searchText.length > 0 || self.filters.count > 0;
334 | if (self.searchEnabled) {
335 | self.searchResultGroupedNodes =
336 | CBCGetSearchResultGroupedNodes(self.groupedNodes, self.filters, self.searchText);
337 | }
338 | [self.tableView reloadData];
339 | [self.tableView layoutIfNeeded];
340 | }
341 |
342 | @end
343 |
344 | static void CBCAddNodeFromBreadCrumbs(CBCNode *tree,
345 | NSArray *breadCrumbs,
346 | Class aClass,
347 | NSDictionary *metadata) {
348 | // Walk down the navigation tree one breadcrumb at a time, creating nodes along the way.
349 |
350 | CBCNode *node = tree;
351 | for (NSUInteger ix = 0; ix < [breadCrumbs count]; ++ix) {
352 | NSString *title;
353 | NSString *group;
354 | id breadCrumb = breadCrumbs[ix];
355 | if ([breadCrumb isKindOfClass:[NSArray class]]) {
356 | NSArray *breadCrumbArray = breadCrumb;
357 | if (breadCrumbArray.count > 1) {
358 | group = breadCrumbArray[0];
359 | title = breadCrumbArray[1];
360 | } else {
361 | title = breadCrumbArray[0];
362 | }
363 | } else {
364 | title = breadCrumb;
365 | }
366 | BOOL isLastCrumb = ix == [breadCrumbs count] - 1;
367 |
368 | // Don't walk the last crumb
369 | if (node.map[title] && !isLastCrumb) {
370 | node = node.map[title];
371 | node.group = group;
372 | continue;
373 | }
374 |
375 | CBCNode *child = [[CBCNode alloc] initWithTitle:title];
376 | [node addChild:child];
377 | node = child;
378 | node.group = group;
379 | }
380 |
381 | // Metadata gets assigned to the leaf node in the tree.
382 | node.metadata = metadata;
383 |
384 | // Gather keywords from the breadcrumbs and keywords metadata for search.
385 | NSMutableSet *keywords = [NSMutableSet set];
386 | for (id crumb in metadata[CBCBreadcrumbs]) {
387 | if ([crumb isKindOfClass:[NSArray class]]) {
388 | for (NSString *word in crumb) {
389 | [keywords addObject:word.lowercaseString];
390 | }
391 | } else {
392 | [keywords addObject:[crumb lowercaseString]];
393 | }
394 | }
395 | for (NSString *keyword in metadata[CBCKeywords]) {
396 | [keywords addObject:keyword.lowercaseString];
397 | }
398 | node.keyWords = [keywords copy];
399 |
400 | if ([[metadata objectForKey:CBCIsDebug] boolValue]) {
401 | tree.debugLeaf = node;
402 | }
403 | node.exampleClass = aClass;
404 | }
405 |
406 | static CBCNode *CBCCreateTreeWithOnlyPresentable(BOOL onlyPresentable) {
407 | NSArray *allClasses = CBCGetAllCompatibleClasses();
408 | NSArray *filteredClasses = [allClasses
409 | filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object,
410 | NSDictionary *bindings) {
411 | if (!CBCCanRunClassOnCurrentOperatingSystem(object)) {
412 | return NO;
413 | }
414 | NSDictionary *metadata = CBCCatalogMetadataFromClass(object);
415 | id breadcrumbs = [metadata objectForKey:CBCBreadcrumbs];
416 | BOOL validObject = breadcrumbs != nil && [breadcrumbs isKindOfClass:[NSArray class]];
417 | NSNumber *isPresentable = [metadata objectForKey:CBCIsPresentable];
418 | // If CBCIsPresentable is not explicitly set in a class's metadata,
419 | // this class is presentable by default.
420 | if (onlyPresentable && isPresentable) {
421 | validObject &= isPresentable.boolValue;
422 | }
423 | return validObject;
424 | }]];
425 |
426 | CBCNode *tree = [[CBCNode alloc] initWithTitle:@"Root"];
427 | for (Class aClass in filteredClasses) {
428 | // Each example view controller defines its own breadcrumbs (metadata[CBCBreadcrumbs]).
429 | NSDictionary *metadata = CBCCatalogMetadataFromClass(aClass);
430 | NSArray *breadCrumbs = [metadata objectForKey:CBCBreadcrumbs];
431 | CBCAddNodeFromBreadCrumbs(tree, breadCrumbs, aClass, metadata);
432 | }
433 |
434 | // Perform final post-processing on the nodes.
435 | NSMutableArray *queue = [NSMutableArray arrayWithObject:tree];
436 | while ([queue count] > 0) {
437 | CBCNode *node = [queue firstObject];
438 | [queue removeObjectAtIndex:0];
439 | [queue addObjectsFromArray:node.children];
440 |
441 | [node finalizeNode];
442 | }
443 |
444 | return tree;
445 | }
446 |
447 | CBCNode *CBCCreateNavigationTree(void) {
448 | return CBCCreateTreeWithOnlyPresentable(NO);
449 | }
450 |
451 | CBCNode *CBCCreatePresentableNavigationTree(void) {
452 | return CBCCreateTreeWithOnlyPresentable(YES);
453 | }
454 |
--------------------------------------------------------------------------------
/example/catalog/Catalog.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 666CA6B51CAE277E001B1884 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 666CA6B41CAE277E001B1884 /* AppDelegate.swift */; };
11 | 666CA6BC1CAE277E001B1884 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 666CA6BB1CAE277E001B1884 /* Assets.xcassets */; };
12 | 666CA6BF1CAE277E001B1884 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 666CA6BD1CAE277E001B1884 /* LaunchScreen.storyboard */; };
13 | 83BA0B8D6103E79970C1227F /* Pods_Catalog_Catalog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24BAEA858EF4F3E92559136E /* Pods_Catalog_Catalog.framework */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXFileReference section */
17 | 121D8E1E6851B9F818032CF3 /* Pods-Catalog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Catalog.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Catalog/Pods-Catalog.debug.xcconfig"; sourceTree = ""; };
18 | 24BAEA858EF4F3E92559136E /* Pods_Catalog_Catalog.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Catalog_Catalog.framework; sourceTree = BUILT_PRODUCTS_DIR; };
19 | 44BC83A9EA60CDCD8C1D2BC4 /* Pods-Catalog-Catalog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Catalog-Catalog.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-Catalog-Catalog/Pods-Catalog-Catalog.debug.xcconfig"; sourceTree = ""; };
20 | 666CA6B11CAE277E001B1884 /* Catalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Catalog.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 666CA6B41CAE277E001B1884 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
22 | 666CA6BB1CAE277E001B1884 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
23 | 666CA6BE1CAE277E001B1884 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
24 | 666CA6C01CAE277E001B1884 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
25 | 78C82408C38B7D685BD9ACB5 /* Pods-Catalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Catalog.release.xcconfig"; path = "Pods/Target Support Files/Pods-Catalog/Pods-Catalog.release.xcconfig"; sourceTree = ""; };
26 | B6E8A428B0228637473AA0C3 /* Pods_Catalog.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Catalog.framework; sourceTree = BUILT_PRODUCTS_DIR; };
27 | DA69943629622BF33AA4667D /* Pods-Catalog-Catalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Catalog-Catalog.release.xcconfig"; path = "../Pods/Target Support Files/Pods-Catalog-Catalog/Pods-Catalog-Catalog.release.xcconfig"; sourceTree = ""; };
28 | /* End PBXFileReference section */
29 |
30 | /* Begin PBXFrameworksBuildPhase section */
31 | 666CA6AE1CAE277E001B1884 /* Frameworks */ = {
32 | isa = PBXFrameworksBuildPhase;
33 | buildActionMask = 2147483647;
34 | files = (
35 | 83BA0B8D6103E79970C1227F /* Pods_Catalog_Catalog.framework in Frameworks */,
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 2D36AAC37535529FA7D50C57 /* Pods */ = {
43 | isa = PBXGroup;
44 | children = (
45 | 121D8E1E6851B9F818032CF3 /* Pods-Catalog.debug.xcconfig */,
46 | 78C82408C38B7D685BD9ACB5 /* Pods-Catalog.release.xcconfig */,
47 | 44BC83A9EA60CDCD8C1D2BC4 /* Pods-Catalog-Catalog.debug.xcconfig */,
48 | DA69943629622BF33AA4667D /* Pods-Catalog-Catalog.release.xcconfig */,
49 | );
50 | name = Pods;
51 | sourceTree = "";
52 | };
53 | 666CA6A81CAE277E001B1884 = {
54 | isa = PBXGroup;
55 | children = (
56 | 666CA6B31CAE277E001B1884 /* Catalog */,
57 | 666CA6B21CAE277E001B1884 /* Products */,
58 | 2D36AAC37535529FA7D50C57 /* Pods */,
59 | 8D3989EE1C77402F56511585 /* Frameworks */,
60 | );
61 | sourceTree = "";
62 | };
63 | 666CA6B21CAE277E001B1884 /* Products */ = {
64 | isa = PBXGroup;
65 | children = (
66 | 666CA6B11CAE277E001B1884 /* Catalog.app */,
67 | );
68 | name = Products;
69 | sourceTree = "";
70 | };
71 | 666CA6B31CAE277E001B1884 /* Catalog */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 666CA6B41CAE277E001B1884 /* AppDelegate.swift */,
75 | 666CA6BB1CAE277E001B1884 /* Assets.xcassets */,
76 | 666CA6BD1CAE277E001B1884 /* LaunchScreen.storyboard */,
77 | 666CA6C01CAE277E001B1884 /* Info.plist */,
78 | );
79 | path = Catalog;
80 | sourceTree = "";
81 | };
82 | 8D3989EE1C77402F56511585 /* Frameworks */ = {
83 | isa = PBXGroup;
84 | children = (
85 | B6E8A428B0228637473AA0C3 /* Pods_Catalog.framework */,
86 | 24BAEA858EF4F3E92559136E /* Pods_Catalog_Catalog.framework */,
87 | );
88 | name = Frameworks;
89 | sourceTree = "";
90 | };
91 | /* End PBXGroup section */
92 |
93 | /* Begin PBXNativeTarget section */
94 | 666CA6B01CAE277E001B1884 /* Catalog */ = {
95 | isa = PBXNativeTarget;
96 | buildConfigurationList = 666CA6C31CAE277E001B1884 /* Build configuration list for PBXNativeTarget "Catalog" */;
97 | buildPhases = (
98 | 0B6A648A1B04FF2E118E2247 /* [CP] Check Pods Manifest.lock */,
99 | 666CA6AD1CAE277E001B1884 /* Sources */,
100 | 666CA6AE1CAE277E001B1884 /* Frameworks */,
101 | 666CA6AF1CAE277E001B1884 /* Resources */,
102 | B3B7B96A36CDFE60760F0D51 /* [CP] Embed Pods Frameworks */,
103 | 526A63F3210C836C577F3F5D /* [CP] Copy Pods Resources */,
104 | );
105 | buildRules = (
106 | );
107 | dependencies = (
108 | );
109 | name = Catalog;
110 | productName = Catalog;
111 | productReference = 666CA6B11CAE277E001B1884 /* Catalog.app */;
112 | productType = "com.apple.product-type.application";
113 | };
114 | /* End PBXNativeTarget section */
115 |
116 | /* Begin PBXProject section */
117 | 666CA6A91CAE277E001B1884 /* Project object */ = {
118 | isa = PBXProject;
119 | attributes = {
120 | LastSwiftUpdateCheck = 0720;
121 | LastUpgradeCheck = 0800;
122 | ORGANIZATIONNAME = Google;
123 | TargetAttributes = {
124 | 666CA6B01CAE277E001B1884 = {
125 | CreatedOnToolsVersion = 7.2.1;
126 | LastSwiftMigration = 0800;
127 | };
128 | };
129 | };
130 | buildConfigurationList = 666CA6AC1CAE277E001B1884 /* Build configuration list for PBXProject "Catalog" */;
131 | compatibilityVersion = "Xcode 3.2";
132 | developmentRegion = English;
133 | hasScannedForEncodings = 0;
134 | knownRegions = (
135 | en,
136 | Base,
137 | );
138 | mainGroup = 666CA6A81CAE277E001B1884;
139 | productRefGroup = 666CA6B21CAE277E001B1884 /* Products */;
140 | projectDirPath = "";
141 | projectRoot = "";
142 | targets = (
143 | 666CA6B01CAE277E001B1884 /* Catalog */,
144 | );
145 | };
146 | /* End PBXProject section */
147 |
148 | /* Begin PBXResourcesBuildPhase section */
149 | 666CA6AF1CAE277E001B1884 /* Resources */ = {
150 | isa = PBXResourcesBuildPhase;
151 | buildActionMask = 2147483647;
152 | files = (
153 | 666CA6BF1CAE277E001B1884 /* LaunchScreen.storyboard in Resources */,
154 | 666CA6BC1CAE277E001B1884 /* Assets.xcassets in Resources */,
155 | );
156 | runOnlyForDeploymentPostprocessing = 0;
157 | };
158 | /* End PBXResourcesBuildPhase section */
159 |
160 | /* Begin PBXShellScriptBuildPhase section */
161 | 0B6A648A1B04FF2E118E2247 /* [CP] Check Pods Manifest.lock */ = {
162 | isa = PBXShellScriptBuildPhase;
163 | buildActionMask = 2147483647;
164 | files = (
165 | );
166 | inputPaths = (
167 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
168 | "${PODS_ROOT}/Manifest.lock",
169 | );
170 | name = "[CP] Check Pods Manifest.lock";
171 | outputPaths = (
172 | "$(DERIVED_FILE_DIR)/Pods-Catalog-Catalog-checkManifestLockResult.txt",
173 | );
174 | runOnlyForDeploymentPostprocessing = 0;
175 | shellPath = /bin/sh;
176 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
177 | showEnvVarsInLog = 0;
178 | };
179 | 526A63F3210C836C577F3F5D /* [CP] Copy Pods Resources */ = {
180 | isa = PBXShellScriptBuildPhase;
181 | buildActionMask = 2147483647;
182 | files = (
183 | );
184 | inputPaths = (
185 | );
186 | name = "[CP] Copy Pods Resources";
187 | outputPaths = (
188 | );
189 | runOnlyForDeploymentPostprocessing = 0;
190 | shellPath = /bin/sh;
191 | shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-Catalog-Catalog/Pods-Catalog-Catalog-resources.sh\"\n";
192 | showEnvVarsInLog = 0;
193 | };
194 | B3B7B96A36CDFE60760F0D51 /* [CP] Embed Pods Frameworks */ = {
195 | isa = PBXShellScriptBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | );
199 | inputPaths = (
200 | "${SRCROOT}/../Pods/Target Support Files/Pods-Catalog-Catalog/Pods-Catalog-Catalog-frameworks.sh",
201 | "${BUILT_PRODUCTS_DIR}/CatalogByConvention/CatalogByConvention.framework",
202 | "${BUILT_PRODUCTS_DIR}/CatalogExamples/CatalogExamples.framework",
203 | "${BUILT_PRODUCTS_DIR}/Resistor/Resistor.framework",
204 | );
205 | name = "[CP] Embed Pods Frameworks";
206 | outputPaths = (
207 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CatalogByConvention.framework",
208 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CatalogExamples.framework",
209 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Resistor.framework",
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | shellPath = /bin/sh;
213 | shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-Catalog-Catalog/Pods-Catalog-Catalog-frameworks.sh\"\n";
214 | showEnvVarsInLog = 0;
215 | };
216 | /* End PBXShellScriptBuildPhase section */
217 |
218 | /* Begin PBXSourcesBuildPhase section */
219 | 666CA6AD1CAE277E001B1884 /* Sources */ = {
220 | isa = PBXSourcesBuildPhase;
221 | buildActionMask = 2147483647;
222 | files = (
223 | 666CA6B51CAE277E001B1884 /* AppDelegate.swift in Sources */,
224 | );
225 | runOnlyForDeploymentPostprocessing = 0;
226 | };
227 | /* End PBXSourcesBuildPhase section */
228 |
229 | /* Begin PBXVariantGroup section */
230 | 666CA6BD1CAE277E001B1884 /* LaunchScreen.storyboard */ = {
231 | isa = PBXVariantGroup;
232 | children = (
233 | 666CA6BE1CAE277E001B1884 /* Base */,
234 | );
235 | name = LaunchScreen.storyboard;
236 | sourceTree = "";
237 | };
238 | /* End PBXVariantGroup section */
239 |
240 | /* Begin XCBuildConfiguration section */
241 | 666CA6C11CAE277E001B1884 /* Debug */ = {
242 | isa = XCBuildConfiguration;
243 | buildSettings = {
244 | ALWAYS_SEARCH_USER_PATHS = NO;
245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
246 | CLANG_CXX_LIBRARY = "libc++";
247 | CLANG_ENABLE_MODULES = YES;
248 | CLANG_ENABLE_OBJC_ARC = YES;
249 | CLANG_WARN_BOOL_CONVERSION = YES;
250 | CLANG_WARN_CONSTANT_CONVERSION = YES;
251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
252 | CLANG_WARN_EMPTY_BODY = YES;
253 | CLANG_WARN_ENUM_CONVERSION = YES;
254 | CLANG_WARN_INFINITE_RECURSION = YES;
255 | CLANG_WARN_INT_CONVERSION = YES;
256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
257 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
258 | CLANG_WARN_UNREACHABLE_CODE = YES;
259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
260 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
261 | COPY_PHASE_STRIP = NO;
262 | DEBUG_INFORMATION_FORMAT = dwarf;
263 | ENABLE_STRICT_OBJC_MSGSEND = YES;
264 | ENABLE_TESTABILITY = YES;
265 | GCC_C_LANGUAGE_STANDARD = gnu99;
266 | GCC_DYNAMIC_NO_PIC = NO;
267 | GCC_NO_COMMON_BLOCKS = YES;
268 | GCC_OPTIMIZATION_LEVEL = 0;
269 | GCC_PREPROCESSOR_DEFINITIONS = (
270 | "DEBUG=1",
271 | "$(inherited)",
272 | );
273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
275 | GCC_WARN_UNDECLARED_SELECTOR = YES;
276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
277 | GCC_WARN_UNUSED_FUNCTION = YES;
278 | GCC_WARN_UNUSED_VARIABLE = YES;
279 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
280 | MTL_ENABLE_DEBUG_INFO = YES;
281 | ONLY_ACTIVE_ARCH = YES;
282 | SDKROOT = iphoneos;
283 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
284 | TARGETED_DEVICE_FAMILY = "1,2";
285 | };
286 | name = Debug;
287 | };
288 | 666CA6C21CAE277E001B1884 /* Release */ = {
289 | isa = XCBuildConfiguration;
290 | buildSettings = {
291 | ALWAYS_SEARCH_USER_PATHS = NO;
292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
293 | CLANG_CXX_LIBRARY = "libc++";
294 | CLANG_ENABLE_MODULES = YES;
295 | CLANG_ENABLE_OBJC_ARC = YES;
296 | CLANG_WARN_BOOL_CONVERSION = YES;
297 | CLANG_WARN_CONSTANT_CONVERSION = YES;
298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
299 | CLANG_WARN_EMPTY_BODY = YES;
300 | CLANG_WARN_ENUM_CONVERSION = YES;
301 | CLANG_WARN_INFINITE_RECURSION = YES;
302 | CLANG_WARN_INT_CONVERSION = YES;
303 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
304 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
305 | CLANG_WARN_UNREACHABLE_CODE = YES;
306 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
307 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
308 | COPY_PHASE_STRIP = NO;
309 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
310 | ENABLE_NS_ASSERTIONS = NO;
311 | ENABLE_STRICT_OBJC_MSGSEND = YES;
312 | GCC_C_LANGUAGE_STANDARD = gnu99;
313 | GCC_NO_COMMON_BLOCKS = YES;
314 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
315 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
316 | GCC_WARN_UNDECLARED_SELECTOR = YES;
317 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
318 | GCC_WARN_UNUSED_FUNCTION = YES;
319 | GCC_WARN_UNUSED_VARIABLE = YES;
320 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
321 | MTL_ENABLE_DEBUG_INFO = NO;
322 | SDKROOT = iphoneos;
323 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
324 | TARGETED_DEVICE_FAMILY = "1,2";
325 | VALIDATE_PRODUCT = YES;
326 | };
327 | name = Release;
328 | };
329 | 666CA6C41CAE277E001B1884 /* Debug */ = {
330 | isa = XCBuildConfiguration;
331 | baseConfigurationReference = 44BC83A9EA60CDCD8C1D2BC4 /* Pods-Catalog-Catalog.debug.xcconfig */;
332 | buildSettings = {
333 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
334 | INFOPLIST_FILE = Catalog/Info.plist;
335 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
336 | PRODUCT_BUNDLE_IDENTIFIER = com.google.Catalog;
337 | PRODUCT_NAME = "$(TARGET_NAME)";
338 | SWIFT_VERSION = 3.0;
339 | };
340 | name = Debug;
341 | };
342 | 666CA6C51CAE277E001B1884 /* Release */ = {
343 | isa = XCBuildConfiguration;
344 | baseConfigurationReference = DA69943629622BF33AA4667D /* Pods-Catalog-Catalog.release.xcconfig */;
345 | buildSettings = {
346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
347 | INFOPLIST_FILE = Catalog/Info.plist;
348 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
349 | PRODUCT_BUNDLE_IDENTIFIER = com.google.Catalog;
350 | PRODUCT_NAME = "$(TARGET_NAME)";
351 | SWIFT_VERSION = 3.0;
352 | };
353 | name = Release;
354 | };
355 | /* End XCBuildConfiguration section */
356 |
357 | /* Begin XCConfigurationList section */
358 | 666CA6AC1CAE277E001B1884 /* Build configuration list for PBXProject "Catalog" */ = {
359 | isa = XCConfigurationList;
360 | buildConfigurations = (
361 | 666CA6C11CAE277E001B1884 /* Debug */,
362 | 666CA6C21CAE277E001B1884 /* Release */,
363 | );
364 | defaultConfigurationIsVisible = 0;
365 | defaultConfigurationName = Release;
366 | };
367 | 666CA6C31CAE277E001B1884 /* Build configuration list for PBXNativeTarget "Catalog" */ = {
368 | isa = XCConfigurationList;
369 | buildConfigurations = (
370 | 666CA6C41CAE277E001B1884 /* Debug */,
371 | 666CA6C51CAE277E001B1884 /* Release */,
372 | );
373 | defaultConfigurationIsVisible = 0;
374 | defaultConfigurationName = Release;
375 | };
376 | /* End XCConfigurationList section */
377 | };
378 | rootObject = 666CA6A91CAE277E001B1884 /* Project object */;
379 | }
380 |
--------------------------------------------------------------------------------