Skip to content

Commit ccaf497

Browse files
committed
seperate PlayTools from PlayCover
1 parent c8b52b5 commit ccaf497

File tree

78 files changed

+9296
-17
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+9296
-17
lines changed

.gitignore

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Xcode
2+
#
3+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4+
5+
.DS_store
6+
7+
## User settings
8+
xcuserdata/
9+
10+
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
11+
*.xcscmblueprint
12+
*.xccheckout
13+
14+
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
15+
build/
16+
DerivedData/
17+
*.moved-aside
18+
*.pbxuser
19+
!default.pbxuser
20+
*.mode1v3
21+
!default.mode1v3
22+
*.mode2v3
23+
!default.mode2v3
24+
*.perspectivev3
25+
!default.perspectivev3
26+
27+
## Obj-C/Swift specific
28+
*.hmap
29+
30+
## App packaging
31+
*.ipa
32+
*.dSYM.zip
33+
*.dSYM
34+
35+
## Playgrounds
36+
timeline.xctimeline
37+
playground.xcworkspace
38+
39+
# Swift Package Manager
40+
#
41+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
42+
# Packages/
43+
# Package.pins
44+
# Package.resolved
45+
# *.xcodeproj
46+
#
47+
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
48+
# hence it is not needed unless you have added a package configuration file to your project
49+
# .swiftpm
50+
51+
.build/
52+
53+
# CocoaPods
54+
#
55+
# We recommend against adding the Pods directory to your .gitignore. However
56+
# you should judge for yourself, the pros and cons are mentioned at:
57+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
58+
#
59+
# Pods/
60+
#
61+
# Add this line if you want to avoid checking in source code from the Xcode workspace
62+
# *.xcworkspace
63+
64+
# Carthage
65+
#
66+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
67+
# Carthage/Checkouts
68+
69+
Carthage/Build/
70+
71+
# Accio dependency management
72+
Dependencies/
73+
.accio/
74+
75+
# fastlane
76+
#
77+
# It is recommended to not store the screenshots in the git repo.
78+
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
79+
# For more information about the recommended setup visit:
80+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
81+
82+
fastlane/report.xml
83+
fastlane/Preview.html
84+
fastlane/screenshots/**/*.png
85+
fastlane/test_output
86+
87+
# Code Injection
88+
#
89+
# After new code Injection tools there's a generated folder /iOSInjectionProject
90+
# https://github.com/johnno1962/injectionforxcode
91+
92+
iOSInjectionProject/
93+
xcuserdata/*

Invocation.swift

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
//
2+
// Dynamic
3+
// Created by Mhd Hejazi on 4/15/20.
4+
// Copyright © 2020 Samabox. All rights reserved.
5+
//
6+
7+
import Foundation
8+
9+
class Invocation: Loggable {
10+
public static var loggingEnabled: Bool = false
11+
var loggingEnabled: Bool { Self.loggingEnabled }
12+
13+
private let target: NSObject
14+
private let selector: Selector
15+
16+
var invocation: NSObject?
17+
18+
var numberOfArguments: Int = 0
19+
var returnLength: Int = 0
20+
var returnType: UnsafePointer<CChar>?
21+
var returnTypeString: String? {
22+
guard let returnType = returnType else { return nil }
23+
return String(cString: returnType)
24+
}
25+
var returnsObject: Bool {
26+
/// `@` is the type encoding for an object
27+
returnTypeString == "@"
28+
}
29+
var returnsAny: Bool {
30+
/// `v` is the type encoding for Void
31+
returnTypeString != "v"
32+
}
33+
lazy var returnedObject: AnyObject? = {
34+
returnedObjectValue()
35+
}()
36+
private(set) var isInvoked: Bool = false
37+
38+
init(target: NSObject, selector: Selector) throws {
39+
self.target = target
40+
self.selector = selector
41+
42+
log(.start)
43+
log("# Invocation")
44+
log("[\(type(of: target)) \(selector)]")
45+
log("Selector:", selector)
46+
47+
try initialize()
48+
}
49+
50+
private func initialize() throws {
51+
/// `NSMethodSignature *methodSignature = [target methodSignatureForSelector: selector]`
52+
let methodSignature: NSObject
53+
do {
54+
let selector = NSSelectorFromString("methodSignatureForSelector:")
55+
let signature = (@convention(c)(NSObject, Selector, Selector) -> Any).self
56+
let method = unsafeBitCast(target.method(for: selector), to: signature)
57+
guard let result = method(target, selector, self.selector) as? NSObject else {
58+
let error = InvocationError.unrecognizedSelector(type(of: target), self.selector)
59+
log("ERROR:", error)
60+
throw error
61+
}
62+
methodSignature = result
63+
}
64+
65+
/// `numberOfArguments = methodSignature.numberOfArguments`
66+
self.numberOfArguments = methodSignature.value(forKeyPath: "numberOfArguments") as? Int ?? 0
67+
log("NumberOfArguments:", numberOfArguments)
68+
69+
/// `methodReturnLength = methodSignature.methodReturnLength`
70+
self.returnLength = methodSignature.value(forKeyPath: "methodReturnLength") as? Int ?? 0
71+
log("ReturnLength:", returnLength)
72+
73+
/// `methodReturnType = methodSignature.methodReturnType`
74+
let methodReturnType: UnsafePointer<CChar>
75+
do {
76+
let selector = NSSelectorFromString("methodReturnType")
77+
let signature = (@convention(c)(NSObject, Selector) -> UnsafePointer<CChar>).self
78+
let method = unsafeBitCast(methodSignature.method(for: selector), to: signature)
79+
methodReturnType = method(methodSignature, selector)
80+
}
81+
self.returnType = methodReturnType
82+
log("ReturnType:", self.returnTypeString ?? "?")
83+
84+
/// `NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: methodSignature]`
85+
let invocation: NSObject
86+
do {
87+
let NSInvocation = NSClassFromString("NSInvocation") as AnyObject
88+
let selector = NSSelectorFromString("invocationWithMethodSignature:")
89+
let signature = (@convention(c)(AnyObject, Selector, AnyObject) -> AnyObject).self
90+
let method = unsafeBitCast(NSInvocation.method(for: selector), to: signature)
91+
guard let result = method(NSInvocation, selector, methodSignature) as? NSObject else {
92+
let error = InvocationError.unrecognizedSelector(type(of: target), self.selector)
93+
log("ERROR:", error)
94+
throw error
95+
}
96+
invocation = result
97+
}
98+
self.invocation = invocation
99+
100+
/// `invocation.selector = selector`
101+
do {
102+
let selector = NSSelectorFromString("setSelector:")
103+
let signature = (@convention(c)(NSObject, Selector, Selector) -> Void).self
104+
let method = unsafeBitCast(invocation.method(for: selector), to: signature)
105+
method(invocation, selector, self.selector)
106+
}
107+
108+
/// `[invocation retainArguments]`
109+
do {
110+
let selector = NSSelectorFromString("retainArguments")
111+
let signature = (@convention(c)(NSObject, Selector) -> Void).self
112+
let method = unsafeBitCast(invocation.method(for: selector), to: signature)
113+
method(invocation, selector)
114+
}
115+
}
116+
117+
func setArgument(_ argument: Any?, at index: NSInteger) {
118+
guard let invocation = invocation else { return }
119+
120+
log("Argument #\(index - 1):", argument ?? "<nil>")
121+
122+
/// `[invocation setArgument:&argument atIndex:i + 2]`
123+
let selector = NSSelectorFromString("setArgument:atIndex:")
124+
let signature = (@convention(c)(NSObject, Selector, UnsafeRawPointer, Int) -> Void).self
125+
let method = unsafeBitCast(invocation.method(for: selector), to: signature)
126+
127+
if let valueArgument = argument as? NSValue {
128+
/// Get the type byte size
129+
let typeSize = UnsafeMutablePointer<Int>.allocate(capacity: 1)
130+
defer { typeSize.deallocate() }
131+
NSGetSizeAndAlignment(valueArgument.objCType, typeSize, nil)
132+
133+
/// Get the actual value
134+
let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: typeSize.pointee)
135+
defer { buffer.deallocate() }
136+
valueArgument.getValue(buffer)
137+
138+
method(invocation, selector, buffer, index)
139+
} else {
140+
withUnsafePointer(to: argument) { pointer in
141+
method(invocation, selector, pointer, index)
142+
}
143+
}
144+
}
145+
146+
func invoke() {
147+
guard let invocation = invocation, !isInvoked else { return }
148+
149+
log("Invoking...")
150+
151+
isInvoked = true
152+
153+
/// `[invocation invokeWithTarget: target]`
154+
do {
155+
let selector = NSSelectorFromString("invokeWithTarget:")
156+
let signature = (@convention(c)(NSObject, Selector, AnyObject) -> Void).self
157+
let method = unsafeBitCast(invocation.method(for: selector), to: signature)
158+
method(invocation, selector, target)
159+
}
160+
161+
log(.end)
162+
}
163+
164+
func getReturnValue<T>(result: inout T) {
165+
guard let invocation = invocation else { return }
166+
167+
/// `[invocation getReturnValue: returnValue]`
168+
do {
169+
let selector = NSSelectorFromString("getReturnValue:")
170+
let signature = (@convention(c)(NSObject, Selector, UnsafeMutableRawPointer) -> Void).self
171+
let method = unsafeBitCast(invocation.method(for: selector), to: signature)
172+
withUnsafeMutablePointer(to: &result) { pointer in
173+
method(invocation, selector, pointer)
174+
}
175+
}
176+
177+
if NSStringFromSelector(self.selector) == "alloc" {
178+
log("getReturnValue() -> <alloc>")
179+
} else {
180+
log("getReturnValue() ->", result)
181+
}
182+
}
183+
184+
private func returnedObjectValue() -> AnyObject? {
185+
guard returnsObject, returnLength > 0 else {
186+
return nil
187+
}
188+
189+
var result: AnyObject?
190+
191+
getReturnValue(result: &result)
192+
193+
guard let object = result else {
194+
return nil
195+
}
196+
197+
/// Take the ownership of the initialized objects to ensure they're deallocated properly.
198+
if isRetainingMethod() {
199+
return Unmanaged.passRetained(object).takeRetainedValue()
200+
}
201+
202+
/// `NSInvocation.getReturnValue()` doesn't give us the ownership of the returned object, but the compiler
203+
/// tries to release this object anyway. So, we are retaining it to balance with the compiler's release.
204+
return Unmanaged.passRetained(object).takeUnretainedValue()
205+
}
206+
207+
private func isRetainingMethod() -> Bool {
208+
/// Refer to: https://bit.ly/308okXm
209+
let selector = NSStringFromSelector(self.selector)
210+
return selector == "alloc" ||
211+
selector.hasPrefix("new") ||
212+
selector.hasPrefix("copy") ||
213+
selector.hasPrefix("mutableCopy")
214+
}
215+
}
216+
217+
public enum InvocationError: CustomNSError {
218+
case unrecognizedSelector(_ classType: AnyClass, _ selector: Selector)
219+
220+
public static var errorDomain: String { String(describing: Invocation.self) }
221+
222+
public var errorCode: Int {
223+
switch self {
224+
case .unrecognizedSelector:
225+
return 404
226+
}
227+
}
228+
229+
public var errorUserInfo: [String: Any] {
230+
var message: String
231+
switch self {
232+
case .unrecognizedSelector(let classType, let selector):
233+
message = "'\(String(describing: classType))' doesn't recognize selector '\(selector)'"
234+
}
235+
return [NSLocalizedDescriptionKey: message]
236+
}
237+
}

0 commit comments

Comments
 (0)