diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ce4be89 --- /dev/null +++ b/.clang-format @@ -0,0 +1,61 @@ +# For options, see http://clang.llvm.org/docs/ClangFormatStyleOptions.html + +# A style complying with Google’s C++ style guide +BasedOnStyle: Google + +# If true, horizontally aligns arguments after an open bracket. +# This applies to round brackets (parentheses), angle brackets and square brackets. +AlignAfterOpenBracket: true + +# If true, horizontally align operands of binary and ternary expressions. +AlignOperands: false + +# Allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false. +AllowAllParametersOfDeclarationOnNextLine: true + +# If true, always break after the template<...> of a template declaration. +AlwaysBreakTemplateDeclarations: true + +# If false, a function call’s arguments will either be all on the same line or will have one line each. +BinPackArguments: false + +# If false, a function call’s or function definition’s parameters will either all be on the same line or will have one line each. +BinPackParameters: false + +# If true, ternary operators will be placed after line breaks. +BreakBeforeTernaryOperators: true + +# Column limit for Obj-C at Google is 100 +ColumnLimit: 100 + +# Indent width for line continuations. +ContinuationIndentWidth: 4 + +# If true, analyze the formatted file for the most common alignment of & and *. PointerAlignment is then used only as fallback. +DerivePointerAlignment: false + +# The number of characters to use for indentation of ObjC blocks. +ObjCBlockIndentWidth: 2 + +# Add a space after @property in Objective-C. +# i.e. use \@property (readonly) instead of \@property(readonly). +ObjCSpaceAfterProperty: false + +# Add a space in front of an Objective-C protocol list +# i.e. use Foo instead of Foo. +ObjCSpaceBeforeProtocolList: true + +# Pointer and reference alignment style. +# ie. Right means * and & align to the variable, not the type. +# (PointerBindsToType looks like it's been deprecated and replaced by PointerAlignment) +PointerBindsToType: false +PointerAlignment: Right + +# If true, spaces are inserted inside container literals (e.g. ObjC array and dict literals). +SpacesInContainerLiterals: true + +# The number of columns used for tab stops. +TabWidth: 2 + +# The way to use tab characters in the resulting file. +UseTab: Never \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8dc7e75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,41 @@ +--- +name: "🐛 Bug Report" +about: Report a reproducible bug or regression. +title: 'Bug: ' +labels: 'bug' + +--- + + + +Application version: + +## Steps To Reproduce + +1. +2. + + + +Link to code example: + + + +## The current behavior + + +## The expected behavior \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..7268caf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: 🛠 Feature request +about: If you have a feature request for the service-locator, file it here. +labels: 'type: enhancement' +--- + +**Feature description** +Clearly and concisely describe the feature. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_template.yml b/.github/PULL_REQUEST_TEMPLATE/bug_template.yml new file mode 100644 index 0000000..7d6a149 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/bug_template.yml @@ -0,0 +1,9 @@ +## Bug description +Clearly and concisely describe the problem. + +## Solution description +Describe your code changes in detail for reviewers. Explain the technical solution you have provided and how it fixes the issue case. + +## Covered unit test cases +- [x] yes +- [x] no \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/feature_template.yml b/.github/PULL_REQUEST_TEMPLATE/feature_template.yml new file mode 100644 index 0000000..ab3978b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature_template.yml @@ -0,0 +1,12 @@ +## Feature description +Clearly and concisely describe the feature. + +## Solution description +Describe your code changes in detail for reviewers. + +## Areas affected and ensured +List out the areas affected by your code changes. + +## Covered unit test cases +- [x] yes +- [x] no \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8159031 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,201 @@ +name: service-locator + +on: + push: + branches: + - main + - dev + pull_request: + paths: + - ".github/workflows/**" + - "ServiceLocator/**" + - "ServiceLocatorTests/**" +jobs: + macOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - xcode: "Xcode_15.0" + runsOn: macos-13 + name: "macOS 13, Xcode 15.0" + - xcode: "Xcode_14.3.1" + runsOn: macos-13 + name: "macOS 13, Xcode 14.3.1" + - xcode: "Xcode_14.2" + runsOn: macOS-12 + name: "macOS 12, Xcode 14.2" + - xcode: "Xcode_14.1" + runsOn: macOS-12 + name: "macOS 12, Xcode 14.1" + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Dependencies + run: brew bundle + - name: Generate project + run: xcodegen generate + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "ServiceLocator" -destination "platform=macOS" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + xcode: true + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + iOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=17.0.1,name=iPhone 14 Pro" + name: "iOS 17.0.1" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=iPhone 14 Pro" + name: "iOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Dependencies + run: brew bundle + - name: Generate project + run: xcodegen generate + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "ServiceLocator" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + xcode: true + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + tvOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=17.0,name=Apple TV" + name: "tvOS 17.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=Apple TV" + name: "tvOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Dependencies + run: brew bundle + - name: Generate project + run: xcodegen generate + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "ServiceLocator" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + xcode: true + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + watchOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=10.0,name=Apple Watch Series 9 (45mm)" + name: "watchOS 10.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=9.4,name=Apple Watch Series 8 (45mm)" + name: "watchOS 9.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + - destination: "OS=8.5,name=Apple Watch Series 7 (45mm)" + name: "watchOS 8.5" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Dependencies + run: brew bundle + - name: Generate project + run: xcodegen generate + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "ServiceLocator" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + xcode: true + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + merge-test-reports: + needs: [iOS, macOS, watchOS, tvOS] + runs-on: macos-13 + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: test_output + - run: xcrun xcresulttool merge test_output/**/*.xcresult --output-path test_output/final/final.xcresult + - name: Upload Merged Artifact + uses: actions/upload-artifact@v4 + with: + name: MergedResult + path: test_output/final + + discover-typos: + name: Discover Typos + runs-on: macOS-12 + env: + DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer + steps: + - uses: actions/checkout@v2 + - name: Discover typos + run: | + export PATH="$PATH:/Library/Frameworks/Python.framework/Versions/3.11/bin" + python3 -m pip install --upgrade pip + python3 -m pip install codespell + codespell --ignore-words-list="hart,inout,msdos,sur" --skip="./.build/*,./.git/*" \ No newline at end of file diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml new file mode 100644 index 0000000..158ca87 --- /dev/null +++ b/.github/workflows/danger.yml @@ -0,0 +1,29 @@ +name: Danger + +on: + pull_request: + types: [synchronize, opened, reopened, labeled, unlabeled, edited] + +env: + LC_CTYPE: en_US.UTF-8 + LANG: en_US.UTF-8 + +jobs: + run-danger: + runs-on: ubuntu-latest + steps: + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1.4 + bundler-cache: true + - name: Checkout code + uses: actions/checkout@v2 + - name: Setup gems + run: | + gem install bundler + bundle install --clean --path vendor/bundle + - name: danger + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} + run: bundle exec danger --verbose \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7801c93..ba0fa40 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,8 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ + +# Other + +*.xcodeproj +*.workspace \ No newline at end of file diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..13a4bba --- /dev/null +++ b/Brewfile @@ -0,0 +1,2 @@ +brew "clang-format" +brew "xcodegen" \ No newline at end of file diff --git a/Brewfile.lock.json b/Brewfile.lock.json new file mode 100644 index 0000000..869a27a --- /dev/null +++ b/Brewfile.lock.json @@ -0,0 +1,101 @@ +{ + "entries": { + "brew": { + "clang-format": { + "version": "17.0.6", + "bottle": { + "rebuild": 0, + "root_url": "https://ghcr.io/v2/homebrew/core", + "files": { + "arm64_sonoma": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:1bb881df31b9f0dd6a85ef97572b31bf8292aa7d05d8f35d49bc830424b3011b", + "sha256": "1bb881df31b9f0dd6a85ef97572b31bf8292aa7d05d8f35d49bc830424b3011b" + }, + "arm64_ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:7835985d5e6edfb05205883c484135120789c78bc3b5eeeddc39d7b5170c6b16", + "sha256": "7835985d5e6edfb05205883c484135120789c78bc3b5eeeddc39d7b5170c6b16" + }, + "arm64_monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:67fbefb432b2cc11d08c14ffb89cb71b1026a83b81c2e7fac089663a053b64c2", + "sha256": "67fbefb432b2cc11d08c14ffb89cb71b1026a83b81c2e7fac089663a053b64c2" + }, + "sonoma": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:e11bb2ee8e4012e08afeb1c2109af21feba56e7225ff6e473e69c8ad2aed36ea", + "sha256": "e11bb2ee8e4012e08afeb1c2109af21feba56e7225ff6e473e69c8ad2aed36ea" + }, + "ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:fcb5fe00f5fca01bbe9aae794a6d4c3459effce8f9906445f44d2991fece69ae", + "sha256": "fcb5fe00f5fca01bbe9aae794a6d4c3459effce8f9906445f44d2991fece69ae" + }, + "monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:1ea4441a6fc772efe6eed7a3e64ca74229753eb2b0d66f5b81ead8eed3ae973e", + "sha256": "1ea4441a6fc772efe6eed7a3e64ca74229753eb2b0d66f5b81ead8eed3ae973e" + }, + "x86_64_linux": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/clang-format/blobs/sha256:35e9c5cc360ace20a6eaa5ee6c1956ba93e32faf67834e4c931f60277f590724", + "sha256": "35e9c5cc360ace20a6eaa5ee6c1956ba93e32faf67834e4c931f60277f590724" + } + } + } + }, + "xcodegen": { + "version": "2.38.0", + "bottle": { + "rebuild": 0, + "root_url": "https://ghcr.io/v2/homebrew/core", + "files": { + "arm64_sonoma": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:422fb8dfbc7e2ed59125d22b4687bb54a1ab3f0ddef044a3875b624121f9be47", + "sha256": "422fb8dfbc7e2ed59125d22b4687bb54a1ab3f0ddef044a3875b624121f9be47" + }, + "arm64_ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:5b2d9dfdf8bc9912ecef48ecc4a03cfb4ba68f35f03c4ab4fc9e893b077f8796", + "sha256": "5b2d9dfdf8bc9912ecef48ecc4a03cfb4ba68f35f03c4ab4fc9e893b077f8796" + }, + "arm64_monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:7a239feca86c46f78ae91d631858d957cb2e7e63ea7230b30f3d618097774bff", + "sha256": "7a239feca86c46f78ae91d631858d957cb2e7e63ea7230b30f3d618097774bff" + }, + "sonoma": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:346164300a7e835f8516c70b25793702bab2437d7e9fb606b5394ab757dab4f5", + "sha256": "346164300a7e835f8516c70b25793702bab2437d7e9fb606b5394ab757dab4f5" + }, + "ventura": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:2bca799f6fee1e679a3f826a9a977449a23f81f02896b22a525056f6cd4a07dd", + "sha256": "2bca799f6fee1e679a3f826a9a977449a23f81f02896b22a525056f6cd4a07dd" + }, + "monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:3e306a4b9ad078c77b61d93090c224304c7dac35ca119808db87792edb983be8", + "sha256": "3e306a4b9ad078c77b61d93090c224304c7dac35ca119808db87792edb983be8" + } + } + } + } + } + }, + "system": { + "macos": { + "ventura": { + "HOMEBREW_VERSION": "4.2.7", + "HOMEBREW_PREFIX": "/usr/local", + "Homebrew/homebrew-core": "api", + "CLT": "", + "Xcode": "15.2", + "macOS": "13.6.3" + } + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..84db2fd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +#Change Log +All notable changes to this project will be documented in this file. + +## [1.0.0](https://github.com/space-code/service-locator/releases/tag/1.0.0) +Released on 2024-02-13. + +#### Added +- Initial release of ServiceLocator. + - Added by [Nikita Vasilev](https://github.com/nik3212). \ No newline at end of file diff --git a/Dangerfile b/Dangerfile new file mode 100644 index 0000000..b266982 --- /dev/null +++ b/Dangerfile @@ -0,0 +1 @@ +danger.import_dangerfile(github: 'space-code/dangerfile') \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..20dff64 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem 'danger' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..0bb6b08 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,68 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + base64 (0.2.0) + claide (1.1.0) + claide-plugins (0.9.2) + cork + nap + open4 (~> 1.3) + colored2 (3.1.2) + cork (0.3.0) + colored2 (~> 3.1) + danger (9.4.3) + claide (~> 1.0) + claide-plugins (>= 0.9.2) + colored2 (~> 3.1) + cork (~> 0.1) + faraday (>= 0.9.0, < 3.0) + faraday-http-cache (~> 2.0) + git (~> 1.13) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) + no_proxy_fix + octokit (>= 4.0) + terminal-table (>= 1, < 4) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-http-cache (2.5.1) + faraday (>= 0.8) + faraday-net_http (3.1.0) + net-http + git (1.19.1) + addressable (~> 2.8) + rchardet (~> 1.8) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + nap (1.1.0) + net-http (0.4.1) + uri + no_proxy_fix (0.1.2) + octokit (8.1.0) + base64 + faraday (>= 1, < 3) + sawyer (~> 0.9) + open4 (1.3.4) + public_suffix (5.0.4) + rchardet (1.8.0) + rexml (3.2.6) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.5.0) + uri (0.13.0) + +PLATFORMS + x86_64-darwin-22 + +DEPENDENCIES + danger + +BUNDLED WITH + 2.4.21 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eac1917 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +all: bootstrap + +bootstrap: hook brew + +brew: + brew bundle check || brew bundle + +hook: + ln -sf ../../hooks/pre-commit .git/hooks/pre-commit + chmod +x .git/hooks/pre-commit + +fmt: + find ServiceLocator -iname '*.h' -o -iname '*.m' | xargs clang-format -i + find ServiceLocatorTests -iname '*.h' -o -iname '*.m' | xargs clang-format -i + +.PHONY: all bootstrap hook brew mint lint fmt \ No newline at end of file diff --git a/README.md b/README.md index e974548..29d8189 100644 --- a/README.md +++ b/README.md @@ -1 +1,77 @@ -# service-locator \ No newline at end of file +

service-locator

+ +

+CodeCov +CodeCov +CodeCov +CI +CodeCov +

+ +## Description +`service-locator` is a framework written in Objective-C that implements a design pattern used in software development to encapsulate the processes involved in obtaining a service with a strong abstraction layer. + +- [Usage](#usage) +- [Requirements](#requirements) +- [Communication](#communication) +- [Contributing](#contributing) +- [Author](#author) +- [License](#license) + +## Usage + +1. Define a protocol: + +```objc +@protocol MyProtocol +@end +``` + +2. Define a class that conforms to this protocol: + +```objc +@interface MyClass: NSObject +@end +``` + +3. Register an object in a registry: + +```objc +#import + +[[NVRegistry sharedInstance] register:@protocol(MyProtocol) scope:NVRegistryScopePrototype factory:factory:^id _Nonnull { + return [[MyClass alloc] init]; +}]; +``` + +4. Resolve an object based on a protocol: + +```objc +#import + +MyClass *myClass = [[NVRegistry sharedInstance] resolve:@protocol(MyProtocol)]; +``` + +## Requirements +- iOS 12.0 / macOS 10.14+ / tvOS 12.0 / watchOS 6.0 +- Xcode 15.0 + +## Communication +- If you **found a bug**, open an issue. +- If you **have a feature request**, open an issue. +- If you **want to contribute**, submit a pull request. + +## Contributing +Bootstrapping development environment + +``` +make bootstrap +``` + +Please feel free to help out with this project! If you see something that could be made better or want a new feature, open up an issue or send a Pull Request! + +## Author +Nikita Vasilev, nv3212@gmail.com + +## License +service-locator is available under the MIT license. See the LICENSE file for more info. \ No newline at end of file diff --git a/ServiceLocator.podspec b/ServiceLocator.podspec new file mode 100644 index 0000000..97e5c77 --- /dev/null +++ b/ServiceLocator.podspec @@ -0,0 +1,17 @@ +Pod::Spec.new do |s| + s.name = 'ServiceLocator' + s.version = '1.0.0' + s.license = 'MIT' + s.summary = 'A screen capture framework' + s.homepage = 'https://github.com/space-code/service-locator' + s.authors = { 'Nikita Vasilev' => 'nv3212@gmail.com' } + s.source = { :git => 'https://github.com/space-code/service-locator.git', :tag => s.version } + s.documentation_url = 'https://github.com/space-code/service-locator' + + s.ios.deployment_target = '12.0' + s.osx.deployment_target = '10.14' + s.tvos.deployment_target = '12.0' + s.watchos.deployment_target = '6.0' + + s.source_files = 'ServiceLocator/**/*.{h,m}' +end \ No newline at end of file diff --git a/ServiceLocator/Classes/Model/NVScope.h b/ServiceLocator/Classes/Model/NVScope.h new file mode 100644 index 0000000..bc66adb --- /dev/null +++ b/ServiceLocator/Classes/Model/NVScope.h @@ -0,0 +1,11 @@ +// +// ServiceLocator +// Copyright © 2024 Space Code. All rights reserved. +// + +#ifndef NVScope_h +#define NVScope_h + +typedef NS_ENUM(NSUInteger, NVScope) { NVRegistryScopeSignleton, NVRegistryScopePrototype }; + +#endif /* NVScope_h */ diff --git a/ServiceLocator/Classes/Registry/Model/Registered/NVRegistered.h b/ServiceLocator/Classes/Registry/Model/Registered/NVRegistered.h new file mode 100644 index 0000000..8d3c15b --- /dev/null +++ b/ServiceLocator/Classes/Registry/Model/Registered/NVRegistered.h @@ -0,0 +1,39 @@ +// +// ServiceLocator +// Copyright © 2024 Space Code. All rights reserved. +// + +#import +#import "NVScope.h" + +NS_ASSUME_NONNULL_BEGIN + +/// An object representing a registered dependency. +@interface NVRegistered : NSObject + +/// The scope type of the registered dependency. +@property(nonatomic, readonly) NVScope type; + +/// The singleton object registered, if applicable. +@property(nonatomic, readonly) id singletonObject; + +/// The block to create a prototype object, if applicable. +@property(nonatomic, readonly) id (^prototypeBlock)(void); + +/// Initializes the instance with a singleton object. +/// +/// @param object The singleton object to initialize with. +/// +/// @return An instance of NVRegistered. +- (instancetype)initWithSingleton:(id)object; + +/// Initializes the instance with a prototype block. +/// +/// @param block The block to create a prototype object. +/// +/// @return An instance of NVRegistered. +- (instancetype)initWithPrototypeBlock:(id (^)(void))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ServiceLocator/Classes/Registry/Model/Registered/NVRegistered.m b/ServiceLocator/Classes/Registry/Model/Registered/NVRegistered.m new file mode 100644 index 0000000..70e2d87 --- /dev/null +++ b/ServiceLocator/Classes/Registry/Model/Registered/NVRegistered.m @@ -0,0 +1,30 @@ +// +// ServiceLocator +// Copyright © 2024 Space Code. All rights reserved. +// + +#import "NVRegistered.h" + +@implementation NVRegistered + +#pragma mark - Initialization + +- (instancetype)initWithSingleton:(id)object { + self = [super init]; + if (self) { + _type = NVRegistryScopeSignleton; + _singletonObject = object; + } + return self; +} + +- (instancetype)initWithPrototypeBlock:(id _Nonnull (^)(void))block { + self = [super init]; + if (self) { + _type = NVRegistryScopePrototype; + _prototypeBlock = [block copy]; + } + return self; +} + +@end diff --git a/ServiceLocator/Classes/Registry/NVRegistry.h b/ServiceLocator/Classes/Registry/NVRegistry.h new file mode 100644 index 0000000..bd6db3c --- /dev/null +++ b/ServiceLocator/Classes/Registry/NVRegistry.h @@ -0,0 +1,40 @@ +// +// ServiceLocator +// Copyright © 2024 Space Code. All rights reserved. +// + +#import +#import "NVScope.h" + +NS_ASSUME_NONNULL_BEGIN + +/// A class for managing protocol-based dependencies. +@interface NVRegistry : NSObject + +/// Returns the shared instance of NVRegistry. ++ (instancetype)sharedInstance; + +/// Registers a factory block for the given protocol and scope. +/// +/// @param aProtocol The protocol to register. +/// @param scope The scope of the dependency. +/// @param factory A block that returns an instance of the dependency. +- (void)register:(Protocol *_Null_unspecified)aProtocol + scope:(NVScope)scope + factory:(id (^)(void))factory; + +/// Resolves and returns an instance for the given protocol. +/// +/// @param aProtocol The protocol to resolve. +/// +/// @return An instance conforming to the given protocol. +- (id)resolve:(Protocol *_Null_unspecified)aProtocol; + +/// Removes a dependency with a given protocol. +/// +/// @param aProtocol The protocol to delete. +- (void)remove:(Protocol *_Null_unspecified)aProtocol; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ServiceLocator/Classes/Registry/NVRegistry.m b/ServiceLocator/Classes/Registry/NVRegistry.m new file mode 100644 index 0000000..8bf8ca5 --- /dev/null +++ b/ServiceLocator/Classes/Registry/NVRegistry.m @@ -0,0 +1,90 @@ +// +// ServiceLocator +// Copyright © 2024 Space Code. All rights reserved. +// + +#import "NVRegistry.h" +#import "NVRegistered.h" + +@interface NVRegistry () + +@property(nonatomic, strong) NSMutableDictionary *registry; + +@end + +@implementation NVRegistry + +#pragma mark - Initialization + ++ (instancetype)sharedInstance { + static dispatch_once_t token; + static id sharedInstance = nil; + dispatch_once(&token, ^{ + sharedInstance = [[[self class] alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init { + if (self = [super init]) { + _registry = [[NSMutableDictionary alloc] init]; + } + return self; +} + +#pragma mark - Internal + +- (void)register:(Protocol *_Null_unspecified)aProtocol + scope:(NVScope)scope + factory:(nonnull id _Nonnull (^)(void))factory { + switch (scope) { + case NVRegistryScopeSignleton: + [self registerSingleton:aProtocol factory:factory]; + break; + case NVRegistryScopePrototype: + [self registerPrototype:aProtocol factory:factory]; + break; + } +} + +- (nonnull id)resolve:(Protocol *_Null_unspecified)aProtocol { + NSString *key = [self makeKey:aProtocol]; + NVRegistered *registered = _registry[key]; + + if (registered != nil) { + switch (registered.type) { + case NVRegistryScopeSignleton: + return registered.singletonObject; + case NVRegistryScopePrototype: + return registered.prototypeBlock(); + } + } + + return nil; +} + +- (void)remove:(Protocol *)aProtocol { + NSString *key = [self makeKey:aProtocol]; + [_registry removeObjectForKey:key]; +} + +#pragma mark - Private + +- (nonnull NSString *)makeKey:(Protocol *_Null_unspecified)aProtocol { + NSString *key = NSStringFromProtocol(aProtocol); + return key; +} + +- (void)registerSingleton:(Protocol *_Null_unspecified)aProtocol + factory:(nonnull id _Nonnull (^)(void))factory { + NSString *key = [self makeKey:aProtocol]; + _registry[key] = [[NVRegistered alloc] initWithSingleton:factory()]; +} + +- (void)registerPrototype:(Protocol *_Null_unspecified)aProtocol + factory:(nonnull id _Nonnull (^)(void))factory { + NSString *key = [self makeKey:aProtocol]; + _registry[key] = [[NVRegistered alloc] initWithPrototypeBlock:factory]; +} + +@end diff --git a/ServiceLocator/ServiceLocator.h b/ServiceLocator/ServiceLocator.h new file mode 100644 index 0000000..8fab7b0 --- /dev/null +++ b/ServiceLocator/ServiceLocator.h @@ -0,0 +1,18 @@ +// +// ServiceLocator +// Copyright © 2024 Space Code. All rights reserved. +// + +#import + +//! Project version number for ServiceLocator. +FOUNDATION_EXPORT double ServiceLocatorVersionNumber; + +//! Project version string for ServiceLocator. +FOUNDATION_EXPORT const unsigned char ServiceLocatorVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like +// #import + +#import +#import diff --git a/ServiceLocatorTests/Helpers/DummyProtocol.h b/ServiceLocatorTests/Helpers/DummyProtocol.h new file mode 100644 index 0000000..3667b31 --- /dev/null +++ b/ServiceLocatorTests/Helpers/DummyProtocol.h @@ -0,0 +1,13 @@ +// +// ServiceLocatorTests +// Copyright © 2024 Space Code. All rights reserved. +// + +#ifndef DummyProtocol_h +#define DummyProtocol_h + +@protocol DummyProtocol +@end +; + +#endif /* DummyProtocol_h */ diff --git a/ServiceLocatorTests/UnitTests/NVRegistryTests.m b/ServiceLocatorTests/UnitTests/NVRegistryTests.m new file mode 100644 index 0000000..40f709d --- /dev/null +++ b/ServiceLocatorTests/UnitTests/NVRegistryTests.m @@ -0,0 +1,140 @@ +// +// ServiceLocatorTests +// Copyright © 2024 Space Code. All rights reserved. +// + +#import +#import +#import "DummyProtocol.h" +#import "NVRegistry.h" + +@interface NVRegistryTests : XCTestCase + +@property(nonatomic, strong) NVRegistry *sut; + +@end + +@implementation NVRegistryTests + +#pragma mark - XCTestCase + +- (void)setUp { + [super setUp]; + _sut = [NVRegistry sharedInstance]; +} + +- (void)tearDown { + _sut = nil; + [super tearDown]; +} + +#pragma mark - Tests + +- (void)test_thatRegistryResolvesSingletonObjects { + // given + [_sut register:@protocol(DummyProtocol) + scope:NVRegistryScopeSignleton + factory:^id _Nonnull { + return [self makeClass:@"Class"]; + }]; + + // when + id obj1 = [_sut resolve:@protocol(DummyProtocol)]; + id obj2 = [_sut resolve:@protocol(DummyProtocol)]; + + // when + XCTAssertEqual(obj1, obj2); +} + +- (void)test_thatRegistryResolvesPrototypeObjects { + // given + [_sut register:@protocol(DummyProtocol) + scope:NVRegistryScopePrototype + factory:^id _Nonnull { + return [self makeClass:@"ClassName"]; + }]; + + // when + id obj1 = [_sut resolve:@protocol(DummyProtocol)]; + id obj2 = [_sut resolve:@protocol(DummyProtocol)]; + + // when + XCTAssertNotEqual(obj1, obj2); +} + +- (void)test_thatRegistryResolvesAFewObjectsWithDifferentProtocols { + // given + [_sut register:[self makeProtocol:@"ProtocolName1"] + scope:NVRegistryScopePrototype + factory:^id _Nonnull { + return [self makeClass:@"ClassName1"]; + }]; + [_sut register:[self makeProtocol:@"ProtocolName2"] + scope:NVRegistryScopePrototype + factory:^id _Nonnull { + return [self makeClass:@"ClassName2"]; + }]; + + // when + id obj1 = [_sut resolve:[self makeProtocol:@"ProtocolName1"]]; + id obj2 = [_sut resolve:[self makeProtocol:@"ProtocolName2"]]; + + // when + XCTAssertNotEqual(obj1, obj2); + XCTAssertEqualObjects(NSStringFromClass([obj1 class]), @"ClassName1"); + XCTAssertEqualObjects(NSStringFromClass([obj2 class]), @"ClassName2"); +} + +- (void)test_thatRegistryResolvesLastRegisteredObject { + // given + [_sut register:@protocol(DummyProtocol) + scope:NVRegistryScopePrototype + factory:^id _Nonnull { + return [self makeClass:@"ClassName1"]; + }]; + [_sut register:@protocol(DummyProtocol) + scope:NVRegistryScopeSignleton + factory:^id _Nonnull { + return [self makeClass:@"ClassName2"]; + }]; + + // when + id obj1 = [_sut resolve:@protocol(DummyProtocol)]; + id obj2 = [_sut resolve:@protocol(DummyProtocol)]; + + // when + XCTAssertEqual(obj1, obj2); + XCTAssertEqualObjects(NSStringFromClass([obj1 class]), @"ClassName2"); +} + +- (void)test_thatRegistryRemovesRegisteredObject { + // given + [_sut register:@protocol(DummyProtocol) + scope:NVRegistryScopePrototype + factory:^id _Nonnull { + return [self makeClass:@"ClassName1"]; + }]; + + // when + [_sut remove:@protocol(DummyProtocol)]; + id obj = [_sut resolve:@protocol(DummyProtocol)]; + + // then + XCTAssertNil(obj); +} + +#pragma mark - Helpers + +- (Protocol *)makeProtocol:(NSString *)name { + Protocol *aProtocol = + objc_allocateProtocol([name cStringUsingEncoding:NSMacOSRomanStringEncoding]); + return aProtocol; +} + +- (Class)makeClass:(NSString *)name { + Class aClass = objc_allocateClassPair( + [NSObject class], [name cStringUsingEncoding:NSMacOSRomanStringEncoding], 0); + return aClass; +} + +@end diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..b415604 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,48 @@ +codecov: + # Require CI to pass to show coverage, default yes + require_ci_to_pass: yes + notify: + # Codecov should wait for all CI statuses to complete, default yes + wait_for_ci: yes + +coverage: + # Coverage precision range 0-5, default 2 + precision: 2 + + # Direction to round the coverage value - up, down, nearest, default down + round: nearest + + # Value range for red...green, default 70...100 + range: "70...90" + + status: + # Overall project coverage, compare against pull request base + project: + default: + # The required coverage value + target: 50% + + # The leniency in hitting the target. Allow coverage to drop by X% + threshold: 5% + + # Only measure lines adjusted in the pull request or single commit, if the commit in not in the pr + patch: + default: + # The required coverage value + target: 85% + + # Allow coverage to drop by X% + threshold: 5% + changes: no + +comment: + # Pull request Codecov comment format. + # diff: coverage diff of the pull request + # files: a list of files impacted by the pull request (coverage changes, file is new or removed) + layout: "diff, files" + + # Update Codecov comment, if exists. Otherwise post new + behavior: default + + # If true, only post the Codecov comment if coverage changes + require_changes: false \ No newline at end of file diff --git a/hooks/pre-commit b/hooks/pre-commit new file mode 100755 index 0000000..abdac63 --- /dev/null +++ b/hooks/pre-commit @@ -0,0 +1,12 @@ +#!/bin/bash + +git diff --diff-filter=d --staged --name-only | grep -e '\.\(h\|m\)' | while read line; do + if [[ $line == *"/Generated"* ]]; then + echo "IGNORING GENERATED FILE: " "$line"; + else + clang-format -style=file -i "${line}"; + git add "$line"; + fi +done + +xcodegen \ No newline at end of file diff --git a/project.yml b/project.yml new file mode 100644 index 0000000..08d21fc --- /dev/null +++ b/project.yml @@ -0,0 +1,58 @@ +name: ServiceLocator +options: + developmentLanguage: en + createIntermediateGroups: true + deploymentTarget: + iOS: 12.0 + macOS: 10.14 + tvOS: 12.0 + watchOS: 6.0 + xcodeVersion: 15.0 +settings: + base: + SWIFT_VERSION: "5.7" + PRODUCT_BUNDLE_IDENTIFIER: com.nikitavasilev.capture-kit + CODE_SIGN_STYLE: automatic + GENERATE_INFOPLIST_FILE: YES +attributes: + ORGANIZATIONNAME: space-code +schemes: + ServiceLocator: + build: + targets: + ServiceLocator: all + run: + config: Debug + test: + gatherCoverageData: true + targets: + - ServiceLocatorTests + coverageTargets: + - ServiceLocator +targets: + ServiceLocator: + type: framework + supportedDestinations: [iOS, tvOS, macOS] + sources: + - path: ServiceLocator + settings: + base: + MARKETING_VERSION: 1.0.0 + CURRENT_PROJECT_VERSION: 1 + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: NO + GENERATE_INFOPLIST_FILE: YES + TARGETED_DEVICE_FAMILY: "1,2,3,4" + SUPPORTED_PLATFORMS: "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator" + ServiceLocatorTests: + type: bundle.unit-test + supportedDestinations: [iOS, tvOS, macOS] + sources: + - ServiceLocatorTests + dependencies: + - target: ServiceLocator + settings: + base: + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: NO + GENERATE_INFOPLIST_FILE: YES + TARGETED_DEVICE_FAMILY: "1,2,3,4" + SUPPORTED_PLATFORMS: "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator" \ No newline at end of file