Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Swift

on: [push]

jobs:
build:

runs-on: macOS-latest

steps:
- uses: actions/checkout@v2
- name: Build
run: swift build -v
- name: Run tests
run: swift test -v
32 changes: 4 additions & 28 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,29 +1,5 @@
# Xcode
.DS_Store
*/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
*.hmap
*.xccheckout
*.xcuserstate

timeline.xctimeline

#CocoaPods
Pods
Tests/Pods
Tests/Podfile.lock

#Carthage
Carthage/Build
/.build
/Packages
/*.xcodeproj
xcuserdata/
34 changes: 29 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
// swift-tools-version:5.1
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
name: "RetroObjective",
platforms: [.macOS(.v10_15), .iOS(.v13), .watchOS(.v6), .tvOS(.v13)],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "RetroObjectiveObjC",
targets: ["RetroObjectiveObjC"]),
.library(
name: "RetroObjective",
targets: ["RetroObjective"]),
.library(
name: "RetroObjectiveCombine",
targets: ["RetroObjectiveCombine"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "RetroObjectiveObjC",
path: "Sources/RetroObjectiveObjC",
publicHeadersPath: ""
),
.target(
name: "RetroObjective",
dependencies: []),
dependencies: [
.target(name: "RetroObjectiveObjC")
]),
.target(
name: "RetroObjectiveCombine",
dependencies: [
.target(name: "RetroObjective")
]),
.testTarget(
name: "RetroObjectiveTests",
dependencies: ["RetroObjective"])
dependencies: [
.target(name: "RetroObjective")
]),
.testTarget(
name: "RetroObjectiveCombineTests",
dependencies: [
.target(name: "RetroObjectiveCombine")
])
]
)
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ scrollView.delegateProxy
print(scrollView.contentOffset)
}
```
## Basic Example with Combine

You can add a new publisher on UIScrollView

```Swift
extension UISCrollView {
var value: AnyPublisher<UISCrollView, Never> {
Publishers.proxyDelegate(delegateProxy, selector: #selector(UIScrollViewDelegate.scrollViewDidScroll(_:)))
.compactMap { $0.value(at: 0) as? UISCrollView }
.eraseToAnyPublisher()
}
}
```

## Contribution
Welcome to fork and submit pull requests!!
Expand Down
5 changes: 3 additions & 2 deletions RetroObjective.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ Pod::Spec.new do |s|
s.watchos.deployment_target = '3.0'
s.requires_arc = true
s.source = { :git => "https://github.com/amine2233/RetroObjective.git", :tag => s.version.to_s }
s.source_files = "Sources/**/*.{h,m,swift}"
s.module_name = s.name
s.source_files = "Sources/**/*.{h,m,swift}"
s.exclude_files = "Sources/RetroObjective/Exports.swift"
s.module_name = s.name
s.swift_version = "5.0"
s.pod_target_xcconfig = {
'SWIFT_VERSION' => s.swift_version.to_s
Expand Down
1,782 changes: 0 additions & 1,782 deletions RetroObjective.xcodeproj/project.pbxproj

This file was deleted.

This file was deleted.

This file was deleted.

1 change: 1 addition & 0 deletions Sources/RetroObjective/Exports.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@_exported import RetroObjectiveObjC
2 changes: 1 addition & 1 deletion Sources/RetroObjective/RetroObjectiveProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

protocol InitializerProtocol: class {
protocol InitializerProtocol: AnyObject {
static var classSelectors: [NSValue: Set<Selector>] { get set }

func lock()
Expand Down
2 changes: 1 addition & 1 deletion Sources/RetroObjective/RetroObjectiveProxyType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

private var associatedKey: UInt8 = 0

public protocol RetroObjectiveProxyType: class {
public protocol RetroObjectiveProxyType: AnyObject {
associatedtype Owner

func resetRetroObjectiveProxyType(owner: Owner)
Expand Down
66 changes: 66 additions & 0 deletions Sources/RetroObjectiveCombine/RetroObjective+Combine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import RetroObjective
#if canImport(Combine)
import Combine

@available(iOS 13.0, *)
extension Publishers {
public typealias RetroObjectiveDelegate = RetroObjectiveProxy & RetroObjectiveProxyType
struct RetroObjective<Delegate: RetroObjectiveDelegate>: Publisher {
public typealias Output = Arguments
public typealias Failure = Never

private let delegate: Delegate
private let selector: Selector

public init(delegate: Delegate, selector: Selector) {
self.delegate = delegate
self.selector = selector
}

public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = SubscriptionRetroObjectiveProxy(
subscriber: subscriber,
delegate: delegate,
selector: selector)
subscriber.receive(subscription: subscription)
}
}
}

@available(iOS 13.0, *)
extension Publishers.RetroObjective {
private final class SubscriptionRetroObjectiveProxy<S: Subscriber, Delegate: RetroObjectiveProxy>: Subscription where S.Input == Arguments {
private var subscriber: S?
weak private var delegate: Delegate?
let selector: Selector

init(subscriber: S, delegate: Delegate, selector: Selector) {
self.subscriber = subscriber
self.delegate = delegate
self.selector = selector
bind()
}

private func bind() {
self.delegate?.receive(selector: selector) {[weak self] arguments in
_ = self?.subscriber?.receive(arguments)
}
}

func request(_ demand: Subscribers.Demand) {
}

func cancel() {
subscriber = nil
}
}
}

@available(iOS 13.0, *)
public extension Publishers {
static func proxyDelegate<Delegate: RetroObjectiveDelegate>(_ delegate: Delegate, selector: Selector) -> AnyPublisher<Arguments, Never> {
Publishers.RetroObjective(delegate: delegate, selector: selector).eraseToAnyPublisher()
}
}

#endif
12 changes: 12 additions & 0 deletions Sources/RetroObjectiveObjC/RetroObjective.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// Header.h
//
//
// Created by Amine Bensalah on 11/02/2022.
//

#ifndef Header_h
#define Header_h


#endif /* Header_h */
71 changes: 71 additions & 0 deletions Tests/RetroObjectiveCombineTests/RetroObjectiveCombineTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// RetroObjective_iOSTests.swift
// RetroObjective iOSTests
//
// Created by Amine Bensalah on 21/01/2020.
//

import XCTest
import Combine
import RetroObjective
@testable import RetroObjectiveCombine

class RetroObjectiveCombineTests: XCTestCase {

var disposeBag = Set<AnyCancellable>()

override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

func testDelegatePublisher() {
var expectationValue: String?
let expectation = self.expectation(description: #function)
let delegateTester = DelegateTester()
delegateTester.combine
.value
.sink { (value) in
expectationValue = value
expectation.fulfill()
}
.store(in: &disposeBag)

let stringTest = "send new element"
delegateTester.testDelegate(stringTest)

waitForExpectations(timeout: 1.0) { error in
if let error = error {
XCTFail(error.localizedDescription)
}
XCTAssertEqual(expectationValue, stringTest)
}
}

func testOtherDelegatePublisher() {
var expectationValue: String?
let expectation = self.expectation(description: #function)
let delegateTester = DelegateTester()
let proxyDelegateTester = TestDelegateProxy.proxy(for: delegateTester)
proxyDelegateTester
.value
.sink { (value) in
expectationValue = value
expectation.fulfill()
}
.store(in: &disposeBag)

let stringTest = "send new element"
delegateTester.testDelegate(stringTest)

waitForExpectations(timeout: 1.0) { error in
if let error = error {
XCTFail(error.localizedDescription)
}
XCTAssertEqual(expectationValue, stringTest)
}
}
}
Loading