Skip to content

Commit

Permalink
Implement objects-factory package
Browse files Browse the repository at this point in the history
  • Loading branch information
ns-vasilev committed Oct 13, 2023
1 parent 230ad44 commit afeeb41
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Change Log
All notable changes to this project will be documented in this file.
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
all: bootstrap

bootstrap: hook
mint bootstrap

hook:
ln -sf ../../hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

mint:
mint bootstrap

lint:
mint run swiftlint

fmt:
mint run swiftformat Sources Tests

.PHONY: all bootstrap hook mint lint fmt
2 changes: 2 additions & 0 deletions Mintfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
nicklockwood/[email protected]
realm/[email protected]
27 changes: 27 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "ObjectsFactory",
platforms: [.iOS(.v13)],
products: [
.library(name: "ObjectsFactory", targets: ["ObjectsFactory"]),
],
dependencies: [],
targets: [
.target(
name: "ObjectsFactory",
dependencies: ["ObjectsFactoryObjC"]
),
.target(
name: "ObjectsFactoryObjC",
dependencies: []
),
.testTarget(
name: "ObjectsFactoryTests",
dependencies: ["ObjectsFactory"]
),
]
)
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,69 @@
# objects-factory
<h1 align="center" style="margin-top: 0px;">objects-factory</h1>

<p align="center">
<a href="https://github.com/space-code/objects-factory/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/space-code/objects-factory?style=flat"></a>
<a href="https://developer.apple.com/swift"><img alt="5.7" src="https://img.shields.io/badge/language-Swift5.7-orange.svg"/></a>
<a href="https://github.com/space-code/objects-factory"><img alt="CI" src="https://github.com/space-code/objects-factory/actions/workflows/ci.yml/badge.svg?branch=main"></a>
<a href="https://github.com/apple/swift-package-manager" alt="objects-factory on Swift Package Manager" title="objects-factory on Swift Package Manager"><img src="https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg" /></a>
</p>

## Description
`objects-factory` helps to create objects for unit tests that have a private initializer.

- [Usage](#usage)
- [Requirements](#requirements)
- [Installation](#installation)
- [Communication](#communication)
- [Contributing](#contributing)
- [Author](#author)
- [License](#license)

## Usage

It's important that the class you want to instantiate is a subclass of `NSObject`.

```swift
import ObjectsFactory

do {
let session = try ObjectsFactory.create(UIScene.self)
let _ = try ObjectsFactory.create(UIWindowScene.self, properties: ["session": session])
} catch {
// Handle an error here
}
```

## Requirements

## Installation
### Swift Package Manager

The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. It is in early development, but `objects-factory` does support its use on supported platforms.

Once you have your Swift package set up, adding `objects-factory` as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.

```swift
dependencies: [
.package(url: "https://github.com/space-code/objects-factory.git", .upToNextMajor(from: "1.0.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, [email protected]

## License
objects-factory is available under the MIT license. See the LICENSE file for more info.
21 changes: 21 additions & 0 deletions Sources/ObjectsFactory/Classes/ObjectsFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// objects-factory
// Copyright © 2023 Space Code. All rights reserved.
//

import ObjectsFactoryObjC

public typealias ObjectsFactory = ObjectsFactoryObjC.ObjectsFactory

public extension ObjectsFactory {
static func create<T: AnyObject>(_ clazz: T.Type, properties: [String: Any] = [:]) throws -> T {
if let instance = ObjectsFactory.createInstance(clazz, properties: properties) as? T {
return instance
}
throw NSError(
domain: "ObjectsFactory",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "Cannot create an instance of \(clazz)"]
)
}
}
6 changes: 6 additions & 0 deletions Sources/ObjectsFactoryObjC/Internal/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
framework module FetchRequest {
umbrella header "ObjectsFactory.h"

export *
module * { export * }
}
28 changes: 28 additions & 0 deletions Sources/ObjectsFactoryObjC/ObjectsFactory.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Flare
// Copyright © 2023 Space Code. All rights reserved.
//

#import "ObjectsFactory.h"

@implementation ObjectsFactory
+ (id)createInstance:(Class)class {
return [[class alloc] init];
}

+(id)createInstance:(Class)class properties:(NSDictionary *)properties {
if (![class isKindOfClass:[NSObject class]]) {
return nil;
}

id object = [self createInstance:class];
if ([object isKindOfClass:[NSObject class]]) {
NSObject *nsObject = object;
[properties enumerateKeysAndObjectsUsingBlock:^(id key, id valueObject, BOOL *stop) {
[nsObject setValue:valueObject forKey :key];
}];
}

return object;
}
@end
16 changes: 16 additions & 0 deletions Sources/ObjectsFactoryObjC/include/ObjectsFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Flare
// Copyright © 2023 Space Code. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

NS_SWIFT_NAME(ObjectsFactory)
@interface ObjectsFactory : NSObject
+(id)createInstance:(Class)class;
+(id)createInstance:(Class)class properties:(NSDictionary *)properties;
@end

NS_ASSUME_NONNULL_END
44 changes: 44 additions & 0 deletions Tests/ObjectsFactoryTests/ObjectsFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// objects-factory
// Copyright © 2023 Space Code. All rights reserved.
//

import ObjectsFactory
import XCTest

final class ObjectsFactoryTests: XCTestCase {
func testThatSceneFactoryCreatesANewWindowSceneInstance() throws {
// when
var resultError: Error?

do {
let session = try ObjectsFactory.create(UIScene.self)
let _ = try ObjectsFactory.create(UIWindowScene.self, properties: ["session": session])
} catch {
resultError = error
}

// then
XCTAssertNil(resultError)
}

func testThatSceneFactoryThrowsAnErrorWhenClassIsNotASubclassOfNSObject() throws {
// when
var resultError: Error?

do {
let _ = try ObjectsFactory.create(TestObject.self)
} catch {
resultError = error
}

// then
XCTAssertNotNil(resultError)
}
}

// MARK: Private

private extension ObjectsFactoryTests {
class TestObject {}
}
38 changes: 38 additions & 0 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash
git diff --diff-filter=d --staged --name-only | grep -e '\.swift$' | while read line; do
if [[ $line == *"/Generated"* ]]; then
echo "IGNORING GENERATED FILE: " "$line";
else
mint run swiftformat swiftformat "${line}";
git add "$line";
fi
done

LINT=$(which mint)
if [[ -e "${LINT}" ]]; then
# Export files in SCRIPT_INPUT_FILE_$count to lint against later
count=0
while IFS= read -r file_path; do
export SCRIPT_INPUT_FILE_$count="$file_path"
count=$((count + 1))
done < <(git diff --name-only --cached --diff-filter=d | grep ".swift$")
export SCRIPT_INPUT_FILE_COUNT=$count

if [ "$count" -eq 0 ]; then
echo "No files to lint!"
exit 0
fi

echo "Found $count lintable files! Linting now.."
mint run swiftlint --use-script-input-files --strict --config .swiftlint.yml
RESULT=$? # swiftline exit value is number of errors

if [ $RESULT -eq 0 ]; then
echo "🎉 Well done. No violation."
fi
exit $RESULT
else
echo "⚠️ WARNING: SwiftLint not found"
echo "⚠️ You might want to edit .git/hooks/pre-commit to locate your swiftlint"
exit 0
fi

0 comments on commit afeeb41

Please sign in to comment.