Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated to Swift 5 #9

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
94 changes: 84 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@

# Created by https://www.gitignore.io/api/xcode,macos,swift,swiftpm,swiftpackagemanager
# Edit at https://www.gitignore.io/?templates=xcode,macos,swift,swiftpm,swiftpackagemanager

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
Expand All @@ -19,7 +52,8 @@ xcuserdata/

## Other
*.moved-aside
*.xcuserstate
*.xccheckout
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
Expand All @@ -32,36 +66,76 @@ timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
.build/
# Add this line if you want to avoid checking in Xcode SPM integration.
.swiftpm/xcode

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# Accio dependency management
Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/screenshots/**/*.png
fastlane/test_output
ObjectiveKit.xcodeproj/project.xcworkspace/xcuserdata/marmelroy.xcuserdatad/UserInterfaceState.xcuserstate
ObjectiveKit.xcodeproj/xcuserdata/marmelroy.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/

### SwiftPackageManager ###
Packages
xcuserdata
*.xcodeproj


### SwiftPM ###


### Xcode ###
# Xcode
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## User settings

## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)

## Xcode Patch
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno

### Xcode Patch ###
**/xcshareddata/WorkspaceSettings.xcsettings

# End of https://www.gitignore.io/api/xcode,macos,swift,swiftpm,swiftpackagemanager
22 changes: 8 additions & 14 deletions ObjectiveKit/ObjectiveClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@

import Foundation

typealias ImplementationBlock = @convention(block) () -> Void

/// An object that allows you to introspect and modify classes through the ObjC runtime.
public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
public class ObjectiveClass<T: NSObject>: ObjectiveKitRuntimeModification {

public var internalClass: AnyClass

Expand All @@ -33,7 +31,7 @@ public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
var ivars = [String]()
let ivarList = class_copyIvarList(internalClass, &count)
for i in (0..<Int(count)) {
let unwrapped = ivarList?[i].unsafelyUnwrapped
let unwrapped = (ivarList?[i]).unsafelyUnwrapped
if let ivar = ivar_getName(unwrapped) {
let string = String(cString: ivar)
ivars.append(string)
Expand All @@ -44,7 +42,6 @@ public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
}
}


/// Get all selectors implemented by the class.
///
/// - Returns: An array of selectors.
Expand All @@ -54,8 +51,8 @@ public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
var selectors = [Selector]()
let methodList = class_copyMethodList(internalClass, &count)
for i in (0..<Int(count)) {
let unwrapped = methodList?[i].unsafelyUnwrapped
if let selector = method_getName(unwrapped) {
if let unwrapped = methodList?[i] {
let selector = method_getName(unwrapped)
selectors.append(selector)
}
}
Expand All @@ -73,8 +70,8 @@ public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
var protocols = [String]()
let protocolList = class_copyProtocolList(internalClass, &count)
for i in (0..<Int(count)) {
let unwrapped = protocolList?[i].unsafelyUnwrapped
if let protocolName = protocol_getName(unwrapped) {
if let unwrapped = protocolList?[i] {
let protocolName = protocol_getName(unwrapped)
let string = String(cString: protocolName)
protocols.append(string)
}
Expand All @@ -92,8 +89,8 @@ public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
var properties = [String]()
let propertyList = class_copyPropertyList(internalClass, &count)
for i in (0..<Int(count)) {
let unwrapped = propertyList?[i].unsafelyUnwrapped
if let propretyName = property_getName(unwrapped) {
if let unwrapped = propertyList?[i] {
let propretyName = property_getName(unwrapped)
let string = String(cString: propretyName)
properties.append(string)
}
Expand All @@ -102,7 +99,4 @@ public class ObjectiveClass <T: NSObject>: ObjectiveKitRuntimeModification {
return properties
}
}

}


12 changes: 4 additions & 8 deletions ObjectiveKit/RuntimeClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import Foundation

/// A class created at runtime.
public class RuntimeClass: NSObject, ObjectiveKitRuntimeModification {

public var internalClass: AnyClass

private var registered: Bool = false

// MARK: Lifecycle
Expand All @@ -22,7 +20,10 @@ public class RuntimeClass: NSObject, ObjectiveKitRuntimeModification {
/// - Parameter superclass: Superclass to inherit from.
public init(superclass: AnyClass = NSObject.classForCoder()) {
let name = NSUUID().uuidString
self.internalClass = objc_allocateClassPair(superclass, name, 0)
guard let internalClass = objc_allocateClassPair(superclass, name, 0) else {
fatalError("The class '\(superclass)' could not be created")
}
self.internalClass = internalClass
}

// MARK: Dynamic class creation
Expand All @@ -41,7 +42,6 @@ public class RuntimeClass: NSObject, ObjectiveKitRuntimeModification {
class_addIvar(self.internalClass, name, size, UInt8(alignment), rawEncoding)
}


/// Register class. Required before usage. Happens automatically on allocate.
public func register() {
if registered == false {
Expand All @@ -57,10 +57,8 @@ public class RuntimeClass: NSObject, ObjectiveKitRuntimeModification {
self.register()
return internalClass.alloc() as! NSObject
}

}


/// Objective Type
///
/// - NSString: NSString
Expand All @@ -87,6 +85,4 @@ public enum ObjectiveType: Int {
case .Void: return "v"
}
}

}

27 changes: 13 additions & 14 deletions ObjectiveKit/RuntimeModification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import Foundation

public protocol ObjectiveKitRuntimeModification {

var internalClass: AnyClass { get }

// MARK: Runtime modification
Expand All @@ -26,50 +25,50 @@ public protocol ObjectiveKitRuntimeModification {
/// - Parameters:
/// - identifier: Selector name.
/// - implementation: Implementation as a closure.
func addMethod(_ identifier: String, implementation: ImplementationBlock)
@discardableResult
func addMethod(_ identifier: String, implementation: @escaping @convention(block) (AnyObject) -> Void) -> Selector

/// Exchange selectors implemented in the current class.
///
/// - Parameters:
/// - aSelector: Selector.
/// - otherSelector: Selector.
func exchangeSelector(_ aSelector: Selector, with otherSelector: Selector)

}

extension ObjectiveKitRuntimeModification {

public func addSelector(_ selector: Selector, from originalClass: AnyClass) {
guard let method = class_getInstanceMethod(originalClass, selector), let implementation = method_getImplementation(method), let typeEncoding = method_getTypeEncoding(method) else {
guard let method = class_getInstanceMethod(originalClass, selector), let typeEncoding = method_getTypeEncoding(method) else {
return
}
let implementation = method_getImplementation(method)
let string = String(cString: typeEncoding)
class_addMethod(internalClass, selector, implementation, string)
}

public func addMethod(_ identifier: String, implementation: ImplementationBlock) {
let blockObject = unsafeBitCast(implementation, to: AnyObject.self)
@discardableResult
public func addMethod(_ identifier: String, implementation: @escaping @convention(block) (AnyObject) -> Void) -> Selector {
let blockObject = unsafeBitCast(implementation, to: NSObject.self)
let implementation = imp_implementationWithBlock(blockObject)
let selector = NSSelectorFromString(identifier)
let encoding = "v@:f"
class_addMethod(internalClass, selector, implementation, encoding)
class_replaceMethod(internalClass, selector, implementation, encoding)
return selector
}

public func exchangeSelector(_ aSelector: Selector, with otherSelector: Selector) {
let method = class_getInstanceMethod(internalClass, aSelector)
let otherMethod = class_getInstanceMethod(internalClass, otherSelector)
guard let method = class_getInstanceMethod(internalClass, aSelector), let otherMethod = class_getInstanceMethod(internalClass, otherSelector) else {
return
}
method_exchangeImplementations(method, otherMethod)
}

}

public extension NSObject {

/// A convenience method to perform selectors by identifier strings.
///
/// - Parameter identifier: Selector name.
public func performMethod(_ identifier: String) {
func performMethod(_ identifier: String) {
perform(NSSelectorFromString(identifier))
}
}

31 changes: 13 additions & 18 deletions ObjectiveKitTests/ObjectiveKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,20 @@
//

import XCTest
import MapKit
@testable import ObjectiveKit

@objc class Subview: UIView {
@objc protocol CustomProtocol { }

dynamic func testSelector() {
print("test selector")
}
class Subview: UIView, CustomProtocol {
@objc var customVar = "something"
@objc let customLet = "something"

dynamic func swizzledSelector(){
print("swizzled selector")
@objc func customSelector() {
print("test selector")
}

}

@objc class ObjectiveKitTests: XCTestCase {

class ObjectiveKitTests: XCTestCase {
let closureName = "random"

dynamic func testSelector() {
Expand All @@ -33,7 +30,7 @@ import MapKit
func testAddClosure() {
let methodExpectation = expectation(description: "Method was called")
let objectiveView = ObjectiveClass<UIView>()
objectiveView.addMethod(closureName, implementation: {
objectiveView.addMethod(closureName, implementation: { _ in
methodExpectation.fulfill()
})
let view = UIView()
Expand All @@ -58,16 +55,14 @@ import MapKit
}

func testIntrospection() {
let objectiveView = ObjectiveClass<MKMapView>()
let objectiveView = ObjectiveClass<Subview>()
let ivars = objectiveView.ivars
XCTAssert(ivars.contains("_camera"))
XCTAssertEqual(ivars, ["customVar", "customLet"])
let selectors = objectiveView.selectors
XCTAssert(selectors.contains(NSSelectorFromString("layoutSubviews")))
XCTAssertEqual(selectors.last, NSSelectorFromString("customSelector"))
let protocols = objectiveView.protocols
XCTAssert(protocols.contains("MKAnnotationManagerDelegate"))
XCTAssertEqual(protocols, ["ObjectiveKitTests.CustomProtocol"])
let properties = objectiveView.properties
XCTAssert(properties.contains("mapRegion"))
XCTAssertEqual(properties, ["customVar", "customLet"])
}


}
Loading