A Swift Runtime library for viewing type info, and the dynamic getting and setting of properties.

Overview

Runtime

Swift 5.0 CocoaPods compatible License

Runtime is a Swift library to give you more runtime abilities, including getting type metadata, setting properties via reflection, and type construction for native swift objects.

TypeInfo

TypeInfo exposes metadata about native Swift structs, protocols, classes, tuples and enums. It captures the properties, generic types, inheritance levels, and more.

Example

Lets say you have a User struct:

struct User {
  let id: Int
  let username: String
  let email: String
}

To get the TypeInfo of User, all that you have to do is:

let info = try typeInfo(of: User.self)

Property Info

Within the TypeInfo object, it contains a list of PropertyInfo which represents all properties for the type. PropertyInfo exposes the name and type of the property. It also allows the getting and setting of a value on an object.

Example

Using the same User object as before first we get the TypeInfo and the property we want.

let info = try typeInfo(of: User.self)
let property = try info.property(named: "username")

To get a value:

let username = try property.get(from: user)

To set a value:

try property.set(value: "newUsername", on: &user)

It's that easy! 🎉

Factory

Runtime also supports building an object from it's Type. Both structs and classes are supported.

To build a User object:

let user = try createInstance(type: User.self)

Function Info

FunctionInfo exposes metadata about functions. Including number of arguments, argument types, return types, and whether it can throw an error.

Example

func doSomething(a: Int, b: Bool) throws -> String { 
  return "" 
}

let info = functionInfo(of: doSomething)

FAQ

Q: When getting and setting a value does it work typeless? (i.e. object casted as Any)

A: Yes! The whole library was designed with working typeless in mind.

Q: When creating a new instance of a class is it still protected by ARC?

A: Yes! The retain counts are set properly so ARC can do its job.

Installation

Cocoapods

Runtime is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Runtime'

Swift Package Manager

You can install Runtime via Swift Package Manager by adding the following line to your Package.swift:

import PackageDescription

let package = Package(
    [...]
    dependencies: [
        .Package(url: "https://github.com/wickwirew/Runtime.git", majorVersion: XYZ)
    ]
)

Contributions

Contributions are welcome and encouraged!

Learn

Want to know how it works? Here's an article on how it was implemented.

Want to learn about Swift memory layout? Mike Ash gave and awesome talk on just that.

License

Runtime is available under the MIT license. See the LICENSE file for more info.

Comments
  • Swift 4.2

    Swift 4.2

    The nominal type descriptor metadata is going away completely in 4.2. So there's no longer a way to get field names and types with the method this package uses. There is a new runtime method swift_getFieldAt to retrieve this data, however it uses C++ std::function and is not callable from Swift. You'd need to create a C++ wrapper that exposes a C API to use this. I've opened a PR on Swift to fix this (and make the method easily callable from Swift), but I'm not sure if they will do anything about it: apple/swift#15565. Thought I would share this info, thanks!

    (copy of https://github.com/Zewo/Reflection/issues/14)

    opened by tanner0101 23
  • On macOS `testClass` crashes in dealloc

    On macOS `testClass` crashes in dealloc

    Looks like something is wrong/different w/ the RC here.

    Test Case '-[RuntimeMacTests.FactoryTests testClass]' started.
    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
      * frame #0: 0x0000000108bfea2d libswiftCore.dylib`_swift_release_dealloc + 13
        frame #1: 0x0000000103d362d4 RuntimeMacTests`FactoryTests.testClass(self=0x000000010390e4a0) at FactoryTests.swift:58
        frame #2: 0x0000000103d368d5 RuntimeMacTests`@objc FactoryTests.testClass() at FactoryTests.swift:0
        frame #3: 0x00007fffb972f0cc CoreFoundation`__invoking___ + 140
        frame #4: 0x00007fffb972ef51 CoreFoundation`-[NSInvocation invoke] + 289
        frame #5: 0x000000010034eb36 XCTest`__24-[XCTestCase invokeTest]_block_invoke.272 + 50
    
    opened by helje5 13
  • Swift 5.0 metadata build errors

    Swift 5.0 metadata build errors

    Getting some build errors in Swift 5, in Metadata.swift and TypeInfo.swift:

        var typeInfoConvertible: TypeInfoConvertible
        
        switch kind {
        case .struct:
            typeInfoConvertible = StructMetadata(type: type) // error: Missing argument for parameter 'metadata' in call
    

    (Those errors pop up for every case.)

    That init is defined here: https://github.com/wickwirew/Runtime/blob/master/Sources/Runtime/Metadata/MetadataType.swift#L61

    extension MetadataType {
        // ...
        init(type: Any.Type) {
            let base = metadataPointer(type: type)
            let metadata = base.advanced(by: valueWitnessTableOffset).raw.assumingMemoryBound(to: Layout.self)
            self.init(type: type, metadata: metadata, base: base)
        }
    }
    

    so I'm not sure why Swift 5 is refusing to see it…

    Note this happens when building the project from scratch, or when building as an SPM dependency.

    Troubleshooting notes:

    • I can resolve the "missing argument" errors if I change the inits to typeInfoConvertible = StructMetadata.init(type: type).
    • However, StructMetadata.swift fails with a error: reference to invalid associated type 'Layout' of type 'StructMetadata', on the init declaration itself.
    • Adding manual typealias Layout = FooMetadata declarations fixes those errors, and allowed me to remove the manual Foo.init(type: type)…
    • but then I get our old friend
    Undefined symbols for architecture x86_64:
      "_swift_getFieldAt", referenced from:
          closure #1 (Swift.Int) -> Runtime.PropertyInfo in Runtime.getProperties(of: Any.Type, offsets: [Swift.Int]) -> [Runtime.PropertyInfo] in GetFieldAt.o
    ld: symbol(s) not found for architecture x86_64
    
    • Huh… swift build from the terminal completes just fine; is this only a problem in Xcode? Is there a problem with my deployment target? I've rebuilt the .xcodeproj with swift package generate-xcodeproj…

    Anywho, here's the branch if you wanna take a look: https://github.com/noahemmet/Runtime/tree/swift5

    opened by noahemmet 11
  • Move cruntime to separate module

    Move cruntime to separate module

    This moves cruntime to a separate module, which allows this framework to be built on Linux. Discussion: https://github.com/wickwirew/Runtime/issues/18#issuecomment-434533969 and https://github.com/noahemmet/Runtime/commit/8d685eb4ff7be23385bac54c320aaa71117b38e3

    • It looked like the last few commits included *.xcodeproj and *.xcworkspace files, but if you'd like, I'm happy to add them to the .gitignore.
    • Tests pass locally on my Mac but I'm not set it up test on Linux; is that something Travis will do automatically?

    Thanks for everyone's help on this!

    opened by noahemmet 8
  • Framework/Test Targets for macOS

    Framework/Test Targets for macOS

    I hope those do not confuse the Cocoapod.

    This stuff makes it build as a Cocoa framework. Building things works fine, but the tests actually crash on macOS (same w/ spm test btw).

    opened by helje5 8
  • Use of unresolved identifier swift_allocObject

    Use of unresolved identifier swift_allocObject

    I installed Runtime 2.1.0 using Cocoapods. When I opened Xcode and ran a clean build, I got the following build error in Runtime's Factory.swift: Use of unresolved identifier 'swift_allocObject.

    Here are the details on my environment:

    • Xcode 10.2 (10E125)
    • Command Line Tools: Xcode 10.2
    • Swift version: Apple Swift version 5.0 (swiftlang-1001.0.69.5 clang-1001.0.46.3)
    • Cocoapods: 1.5.3 (also tried 1.6.1 with the same results)
    • Runtime version: 2.1.0 (also tried 2.0.0 with the same results)

    Here are the troubleshooting steps I've tried so far:

    • Removed the post_install from my Podfile, and ran pod install again after deleting the Pods/ directory and Podfile.lock
    • Tried different versions of Runtime, and 2 versions of Cocoapods (described above)
    • Deleted DerivedData

    Screenshot for more context: Runtime-build-error

    opened by AnthonyOliveri 5
  • Fix swift 5.4 linux compatibility

    Fix swift 5.4 linux compatibility

    As described in https://github.com/wickwirew/Runtime/issues/92, swift 5.4 change the ClassMetadataLayout for platforms that do not support Objective-C interoperability. This resulted in Runtime crashing when running on those platforms with the newest swift releases.

    This PR fixes this issue by adjusting the ClassMetadataLayout on those platforms. I'm using the #if !swift(>=5.4) || canImport(Darwin). Not entirely sure if canImport(Darwin) is the proper check for availability of Objective-C interoperability (works though). Happy for any feedback.

    I have another PR ready which adds some CI integration with GitHub to test the framework on all the available linux distortions. This PR passes all tests on all platforms: Actions Run. I chose to do the CI PR separately so this fix can get merged faster.

    opened by Supereg 4
  • Issue with NominalMetadataType.genericArguments()

    Issue with NominalMetadataType.genericArguments()

    Hi,

    I've been trying to get the GraphQL related https://github.com/GraphQLSwift/Graphiti project upgraded to Swift5 using the latest version of this library and came across a crash running the tests. The crash occurs in NominalMetadataType.genericArguments() when trying to get the TypeInfo for a simple Struct. I noticed that the Struct was declared nested within a closure. It appears that NominalMetadataType.genericArguments() is coming back with a non-zero size results when it ought to be zero, and sometimes crashes.

    To try and reproduce the problem within this project directly I've added a new test to MetadataTests.swift called testNestedStruct.

    Please see this branch on my fork https://github.com/SportlabsTechnology/Runtime/tree/investigateCrash

    Weirdly the addition of this new test causes the existing testStruct test to detect a non-zero amount of genericArguments and fail.

    Any ideas ands help appreciated. Thanks, Mike.

    opened by sportlabsMike 4
  • crash occurs if the base class is NSObject

    crash occurs if the base class is NSObject

    class User: NSObject {
        let id: Int = 0
        let username: String = ""
        let email: String = ""
    }
    
    let info = try typeInfo(of: User.self) // crash occurs 
    let property = try info.property(named: "username")
    print("name: \(property.name)")
    print("type: \(property.type)")
    

    how fix this problem? and is there any plan to support objective-c language?

    opened by thekan23 4
  • Broken on Swift 4.1

    Broken on Swift 4.1

    Swift 4.1 apparently includes ABI changes that cause this library to fail completely. ~All tests crash with EXC_BAD_ACCESS.

    Instructions for grabbing the latest Swift 4.1 snapshot here:

    https://gist.github.com/tanner0101/cdb77c7f58d53af2ba2da5d39415389a

    opened by jseibert 4
  • Fix memory leak with setters

    Fix memory leak with setters

    This PR aims to fix issue #64, which reports a memory leak when setting a field which stores a reference type.

    Unfortunately, I'm not very familiar with raw memory manipulation, so I'm not 100% sure whether this is the correct thing to do in all cases, but it appears to address the issue when I apply this fix in our project.

    I wasn't able to run the tests locally, but I'm happy to look into any tests this PR breaks.

    opened by blindmonkey 3
  • Is it possible to get the enum case values/payloads?

    Is it possible to get the enum case values/payloads?

    enum MyEnum: String {
        case A = "test"
        case B
        case C
    }
    
    struct Name {
        var a: MyEnum
        var b: MyEnum
    }
    
    Name(a: .A, b: .B)
    
    let info = try! typeInfo(of: MyEnum.self)
    print(info)
    

    TypeInfo(kind: Runtime.Kind.enum, name: "MyEnum", type: SwiftDump.MyEnum, mangledName: "MyEnum", properties: [], inheritance: [], size: 1, alignment: 1, stride: 1, cases: [Runtime.Case(name: "A", payloadType: nil), Runtime.Case(name: "B", payloadType: nil), Runtime.Case(name: "C", payloadType: nil)], numberOfEnumCases: 3, numberOfPayloadEnumCases: 0, genericTypes: [])

    This is probably just a misunderstanding of mine, but I thought the payload for the A case would be "test"?

    opened by blacktop 1
  • Is it possible to have support to closure replacement?

    Is it possible to have support to closure replacement?

    I couldn't find such information anywhere and I'm a bit stuck on this one issue. I've been trying to write a new closure over an existing closure but I haven't been successful.

    This is how you can reproduce it.

    func closureReplacementTest() {
        
        do {
            
            // Initialize sample data
            var freshFoo = Foo()
            let freshBar = Bar()
            
            // Work on getting the property
            let objectToTest = try typeInfo(of: Foo.self)
            let property = try objectToTest.property(named: "closure")
            
            // Replace the closure to give 100 scores instead
            try property.set(value: { (bar: Bar) in
                bar.score = 100 // Actually crashing
            }, on: &freshFoo)
            
            // Execute what's supposed to be the new closure
            freshFoo.closure(freshBar)
            
            print("Score is: \(freshBar.score)") // Expects it to be 100
    
        } catch {}
    }
    

    I spend a few days reading posts and documentation regarding memory management but unfortunately that's not my forte. =)

    I did debug as best as I could and I noticed that during the crash the memory address of the bar variable is different that it would be when I simply run the block directly. The only thing I think it's not working quite well is the address of the variables of the closure because that's when the crash happens as the code itself of the closure seems to run just fine.

    Strongly appreciate any input on this, I'll still going to spend more days trying to figure out. Thanks!

    opened by samcortez 0
  • Add support for Swift objects inherited from objective-c classes

    Add support for Swift objects inherited from objective-c classes

    Hi, I tried to use the Runtime and faced an issue with an object that inherits from the UIView. Property offsets were wrong. When I dug deeper I found that offsets don't count the instance size of UIView itself. I was able to workaround by modifying ClassMetadata's toTypeInfo() method like this:

    mutating func toTypeInfo() -> TypeInfo {
        var info = TypeInfo(metadata: self)
        info.mangledName = mangledName()
        info.properties = properties()
        info.genericTypes = Array(genericArguments())
        
        var _sc: ClassMetadata? = self
        while var sc = _sc?.superClassMetadata()?.asClassMetadata() {
            info.inheritance.append(sc.type)
            let superInfo = sc.toTypeInfo()
            info.properties.append(contentsOf: superInfo.properties)
            _sc = sc
        }
        
        // Fix offset because of the obj-c inheritance if needed.
        // Fix own properties only.
        if let sc = _sc, let superClass = pointer.pointee.superClass as? AnyObject.Type {
            let size = class_getInstanceSize(superClass) - class_getInstanceSize(NSObject.self)
            for i in 0..<numberOfFields() {
                info.properties[i].offset += numericCast(size) // <--- I also made the offset writable to simplify things
            }
        }
        
        return info
    }
    

    I doubt it's the proper way of doing it and I was just curious If the issue can be fixed. Anyway, is it possible to add support for Swift objects inherited from objective-c classes?

    opened by anton-plebanovich 0
  • Is it possible to support @propertyWrapper?

    Is it possible to support @propertyWrapper?

    Hi there! Thanks for an awesome library! Lately I faced the problem with @propertyWrapper. I have tried to create a @propertyWrapper:

    @propertyWrapper public struct Blob {
        public init(length: Int) {
            self.length = length
            self.wrappedValue = []
        }
        
        let length: Int
        
        public var wrappedValue: [UInt8]
    }
    

    Then I used it in my struct just like this one:

    struct MyStruct {
      @Blob(length: 10) var blob: [UInt8]
      ...
    }
    

    And then I tried to createInstance , i got length = 0, I could not create instance with default length (=10). How to reflect the propertyWrapper and get the default length (= 10)? Thanks

    opened by bigearsenal 1
  • Initialize Enums and Tuple

    Initialize Enums and Tuple

    This PR adds the functionality to initialize enums and tuples using the createInstance function that is currently missing in the runtime framework as documented in #90 .

    Testing

    Adds unit tests to create enums and tuples

    opened by PSchmiedmayer 3
Releases(2.2.4)
Owner
Wes Wickwire
Wes Wickwire
Key-Value Coding (KVC) for native Swift classes and structs

SwiftKVC SwiftKVC brings key-value coding to native Swift classes and structures. You can easily set and access properties just using a subscript: var

Brad Hilton 136 Jun 14, 2022
Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift

EVReflection General information At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in

Edwin Vermeer 964 Dec 14, 2022
A document-based SwiftUI application for viewing and editing EEG data, aimed at making software for viewing brain imaging data more accessible.

Trace A document-based SwiftUI application for viewing and editing EEG data, aimed at making software for viewing brain imaging data more accessible.

Tahmid Azam 3 Dec 15, 2022
Appfiguratesdk - Appfigurate provides the ability to change configuration properties in iOS and watchOS, apps and app extensions, securely, at runtime.

Appfigurateâ„¢ Appfigurate provides the ability to change configuration properties in iOS and watchOS, apps and app extensions, securely, at runtime. Do

Electric Bolt 21 Dec 14, 2022
Swift-compute-runtime - Swift runtime for Fastly Compute@Edge

swift-compute-runtime Swift runtime for Fastly Compute@Edge Getting Started Crea

Andrew Barba 57 Dec 24, 2022
Runtime Mobile Security (RMS) 📱🔥 - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

Runtime Mobile Security (RMS) ?? ?? by @mobilesecurity_ Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to

Mobile Security 2k Dec 29, 2022
React Native utility library around image and video files for getting metadata like MIME type, timestamp, duration, and dimensions. Works on iOS and Android using Java and Obj-C, instead of Node 🚀.

Qeepsake React Native File Utils Extracts information from image and video files including MIME type, duration (video), dimensions, and timestamp. The

Qeepsake 12 Oct 19, 2022
Compatible backports of commonly used type properties for `URL` that are only available from iOS 16.0+ / macOS 13.0+ / tvOS 16.0+ / watchOS 9.0+.

URLCompatibilityKit URLCompatibilityKit is a lightweight Swift package that adds compatible backports of commonly used type properties, type and insta

Marco Eidinger 17 Dec 8, 2022
Type-safe networking abstraction layer that associates request type with response type.

APIKit APIKit is a type-safe networking abstraction layer that associates request type with response type. // SearchRepositoriesRequest conforms to Re

Yosuke Ishikawa 1.9k Dec 30, 2022
A phantom type is a custom type that has one or more unused type parameters.

PhantomTypes A phantom type is a custom type that has one or more unused type parameters. Phantom types allow you to enforce type-safety without sacri

null 3 Nov 4, 2022
Swift library to easily check the current device and some more info about it.

Usage To run the example project, clone the repo, and run pod install from the Example directory first. let device = Deviice.current device is a Devi

Andrea Mario Lufino 56 Nov 3, 2022
Swift library to easily check the current device and some more info about it.

Usage To run the example project, clone the repo, and run pod install from the Example directory first. let device = Deviice.current device is a Devi

Andrea Mario Lufino 56 Nov 3, 2022
A trivial app for storing and viewing famous quotes

Paraphrase A trivial app for storing and viewing famous quotes This app was specifically designed to accompany a tutorial series about modern app infr

Leopold Lemmermann 0 Dec 15, 2021
OutRun is an iOS app for recording and viewing your outdoor workouts

OutRun is an iOS app for recording and viewing your outdoor workouts. Despite the name it supports not just running, but also walking, hiking, cycling and skating.

Tim Fraedrich 195 Dec 28, 2022
An iOS/tvOS photo gallery viewer, useful for viewing a large (or small!) number of photos.

This project is unmaintained. Alex passed away in an accident in late 2019. His love of iOS development will always be remembered. AXPhotoViewer AXPho

Alex Hill 596 Dec 30, 2022
A chromeless web browser for viewing prototypes.

Frameless Frameless is a web browser for iOS 8+ that gets out of your way. It was built for designers and prototypers who need to interact with your p

Jay Stakelon 644 Dec 12, 2022
A modern photo viewing experience for iOS.

NYTPhotoViewer NYTPhotoViewer is a slideshow and image viewer that includes double-tap to zoom, captions, support for multiple images, interactive fli

The New York Times 2.8k Jan 5, 2023
Album CovAR: an interactive AR experience for viewing album covers

Album CovAR - iOS Album CovAR is an interactive AR experience for viewing album covers. The app displays additional information about an album includi

null 0 Dec 14, 2021
iPhone app built with React Native for viewing houses for sale in the Northwest

Den is an iPhone app built with React Native for viewing houses for sale in the NW from rmls.com. After being frustrated with a site that hasn't changed since 2004, I decided to build a better viewing experience in the phone.

Asa Miller 464 Aug 31, 2022
Implement dynamic JSON decoding within the constraints of Swift's sound type system by working on top of Swift's Codable implementations.

DynamicCodableKit DynamicCodableKit helps you to implement dynamic JSON decoding within the constraints of Swift's sound type system by working on top

SwiftyLab 15 Oct 16, 2022