Koan-Sin Tan, freedom@computer.org, Aug 10th, 2025, COSCUP 2025
Learning Swift: A Stupid Way
1
• feel free to interrupt me
a
nytime
2
About me
• Le
a
rnt to use open-source softw
a
re
before the term “open source” w
a
s
coined on VAX-11/780.
• Le
a
rnt some Sm
a
llT
a
lk-80 in e
a
rly
1990s.
• Le
a
rnt some Objective-C progr
a
mming
on NeXT m
a
chines.
• W
a
s excited to he
a
r “Swift is the
Objective-C without the C”. Thought it’s
more Sm
a
llT
a
lk-80 like. You know it’s
not.
• Know very little Swift
3
http://gunkies.org/w/images/c/c1/DEC-VAX-11-780.jpg
using Swift API from PrivateFrameworks
• Some new Apple fr
a
meworks
a
re Swift only, either public or priv
a
te fr
a
meworks
• CoreML: Objective-C
a
nd Swift
• Found
a
tionModels, ModelC
a
t
a
log: Swift only
4
Why using Swift APIs in Apple’s private frameworks is an issue?
• Although there m
a
ny “public symbols” we c
a
n use / link, there
a
re no decl
a
r
a
tions.
• For Objective-C, there
a
re sever
a
l cl
a
ss-dump tools
a
v
a
il
a
ble on the internet.
• Find inform
a
tion from
a
compiled M
a
ch-O
f
ile, the execut
a
ble form
a
t used by m
a
cOS, iOS,
a
nd other Apple oper
a
ting systems, by
a
n
a
lyzing the Objective-C runtime inform
a
tion
embedded in it
a
nd gener
a
tes corresponding Objective-C he
a
der
f
iles.
• There
a
re no tools
a
re good
a
s cl
a
ss-dump for dump Swift API to .swiftinterf
a
ce, which we’ll
expl
a
in l
a
ter.
• If you’re f
a
mili
a
r with the Objective-C runtime API, you might wonder if we c
a
n use it to
dyn
a
mic
a
lly
a
ccess construct Swift objects. Altern
a
tively, is there
a
corresponding “Swift runtime
API”?
5
public vs. private frameworks
• public fr
a
meworks, such
a
s CoreML,
come with C/C++/Objective-C he
a
ders in
He
a
ders
a
nd Swift decl
a
r
a
tions in
Modules.
• priv
a
te fr
a
meworks h
a
ve none of them
• if you don’t know .tbd, it’s Apple’s (text-
b
a
sed dyn
a
mic libr
a
ry stu
ff
). While there
isn't one single "o
ff
ici
a
l speci
f
ic
a
tion
document" re
a
dily
a
v
a
il
a
ble to the public,
there
a
re open-source implement
a
tions
in LLVM.
6
swiftinterface
• swiftinterf
a
ce
f
iles
a
re
a
key component of Swift's Module St
a
bility
a
nd Libr
a
ry Evolution fe
a
tures. They
a
re
a
textu
a
l represent
a
tion of
a
Swift module's public API, gener
a
ted by the Swift compiler during the build process from the origin
a
l Swift source code.
• The .swiftinterf
a
ce
f
ile cont
a
ins:
• Decl
a
r
a
tions: The n
a
mes of
a
ll public types, functions,
a
nd v
a
ri
a
bles.
• Type Inform
a
tion: The sign
a
tures of these public members, including their return types, p
a
r
a
meter types,
a
nd
a
ny generic
constr
a
ints.
• Module-Speci
f
ic Inform
a
tion: Det
a
ils th
a
t
a
llow the compiler to link
a
g
a
inst the module correctly.
• This
f
ile is essenti
a
lly
a
"st
a
ble ABI he
a
der" for Swift modules, ensuring th
a
t
a
libr
a
ry compiled with one version of the Swift compiler
c
a
n be used by
a
n
a
pplic
a
tion compiled with
a
di
ff
erent, comp
a
tible version of the compiler.
• When
a
Swift bin
a
ry is compiled into
a
M
a
ch-O
f
ile, the origin
a
l source code
a
nd the det
a
iled type inform
a
tion in the .swiftinterf
a
ce
f
ile
a
re not preserved. While the M
a
ch-O bin
a
ry cont
a
ins
a
signi
f
ic
a
nt
a
mount of symbol
a
nd met
a
d
a
t
a
, it is not enough to reverse-
engineer the complete
a
nd
a
ccur
a
te Swift-level API.
7
C/C++/Objective-C/Swift symbols on iOS/macOS
• C: no function sign
a
tures, no nothing
• C++: function p
a
r
a
meters type in m
a
ngled symbols, vt
a
ble, etc. The return types
a
re not in
m
a
ngled n
a
mes.
• Objective-C: dyn
a
mic type (generic object id, dyn
a
mic binding, etc.) so there
a
re m
a
ny
inform
a
tion. cl
a
ss-dump somewh
a
t showed wh
a
t we c
a
n from bin
a
ry
f
iles.
• Swift: Swift n
a
me m
a
ngling spec
a
nd “swift dem
a
ngle”
a
re quite comprehensive.
8
modelcatalogdump
9
Using ModelCatalog.framework
• With the help of otool
a
nd lldb, we’ve discovered th
a
t the modelc
a
tlogdump utilizes the
priv
a
te fr
a
mework of the ModelC
a
t
a
log.fr
a
mework.
• The ModelC
a
t
a
log fr
a
mework o
ff
ers very limited Objective-C APIs.
• Interestingly, the object cl
a
sses _TtC….
a
re
a
ctu
a
lly Swift cl
a
sses.
• The _OBJC_CLASS_$_MCResourceInform
a
tion
a
nd
_OBJC_CLASS_$_MCResourceSt
a
tus cl
a
sses don’t provide
a
ny useful inform
a
tion. We’ll
revisit this l
a
ter.
• Therefore, we need to ex
a
mine the Swift API.
• Let’s check if we do “import ModelC
a
t
a
log” in Swift
10
11
12
13
LLM
a
nd Di
ff
usion rel
a
ted cl
a
sses in symbol t
a
ble
import ModelCatalog
Let's check if we can make
import ModelCatalog
in Swift work.
Let's use stu
ff
in
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks
By default, it doesn't work
$ swiftc test_import_mc_1.swift -F /Library/Developer/CommandLineTools/SDKs/
MacOSX.sdk/System/Library/PrivateFrameworks
test_import_mc_1.swift:1:8: error: no such module 'ModelCatalog
1 | import ModelCatalog
| `- error: no such module 'ModelCatalog'
2 |
$
14
import ModelCatalog (cont’d)
• It seems module.modulesm
a
p is needed
• it seems there is not
a
single, comprehensive "o
ff
ici
a
l" Apple document
a
tion for the module.modulem
a
p
f
ile form
a
t. We
f
ind
inform
a
tion in llvm/cl
a
ng document
a
tions
a
nd source code.
• Since we
a
re le
a
rning in
a
stupid w
a
y. Let’s use copy-
a
nd-p
a
ste
a
nd tri
a
l-
a
nd-error.
I copied module.modulemap from CoreML.framework and change CoreML to ModelCatalog,
framework module ModelCatalog {
umbrella header "ModelCatalog.h"
export *
module * { export * }
}
The ModelCatalog.h could be an empty one. That is, we could create an empty
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/ModelCatalog.framework/Headers/ModelCatalog.h
then we can compile the one line ("import ModelCatalog") program successfully.
15
import ModelCatalog (cont’d)
Can we ingore the
umbrella header "ModelCatalog.h"
line, it seems this is for Objective-C to Swift bridge (for Swift code to use Objective-C binary code). YES, if we remove the line only, we got messages like
$ swiftc test_import_mc_1.swift -F /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/ModelCatalog.framework/Modules/
module.modulemap:4:12: error: inferred submodules require a module with an umbrella
2 |
3 | export *
4 | module * { export * }
| `- error: inferred submodules require a module with an umbrella
5 | }
6 |
It seems line 4 here
4 | module * { export * }
is also for using Objective-C. When we remove it too, it WORKS. That is
framework module ModelCatalog {
export *
}
is enough.
16
As we know, /usr/bin/modelcatalogdump could be a good starting point.
By setting breakpoints with b -r ModelCatalog in lldb modelcatalogdump, the
fi
rst method I captured was
ModelCatalog`static ModelCatalog.CatalogClient.generativeExperienceEssentialResourcesStatus() ->
ModelCatalog.ResourceReadinessStatus
By examining exported symbols with nm and swift demangle, we know that ModelCatalog.CatalogClient is a class in
the ModelCatalog framework and
static ModelCatalog.CatalogClient.generativeExperienceEssentialResourcesStatus() ->
ModelCatalog.ResourceReadinessStatus
is a class method of CatalogClient. It takes no argument and returns an instance
of ModelCatalog.ResourceReadinessStatus, which is a Swift enum.
17
18
as
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/ModelCatalog.framework/Modules/
ModelCatalog.swiftmodule/arm64e-apple-macos.swiftinterface
we can compile and run the following simple swift code
import ModelCatalog
print("CatalogClient readiness:", CatalogClient.generativeExperienceEssentialResourcesStatus()
That is, we
fi
gured out how to call undocumented Swift API in /System/Library/PrivateFrameworks/. Note that the
fi
rst 3 lines starting with // are necessary. I copied them from
CoreML.framework and change CoreML to ModelCatalog.
swift demangle example
19
Tools
• So f
a
r, we use nm, otool, lldb,
a
nd swift dem
a
ngle. Are there
a
ny other convenient open-source tools.
• Yes, to me,
a
t le
a
st Ghidr
a
a
nd ipsw
a
re quite useful.
• Ghidr
a
is
a
n open-source reversing tool from NSA. It could
•
a
n
a
lyze dyld_sh
a
red_c
a
che, dissemble functions, decompile m
a
chine code into C/C++ like functions.
• ipsw is kind
a
“iOS/m
a
cOS Rese
a
rch Swiss Army Knife”, I used it to
• downlo
a
d Apple .ipsw
f
iles, extr
a
ct
a
nd re
a
d dyld_sh
a
red_c
a
che from Apple .ipsw
f
ile,
a
nd decompile some functions
with Github Copilot
• ipsw
a
lso provides Objective-C cl
a
ss-dump
a
nd swift-dump
• Unfortun
a
tely, swift-dump is not complete. E.g,, it could not gener
a
te .sw
f
itinterf
a
ce
f
iles
a
nd didn’t h
a
ndle generic type
well.
20
CatalogClient by ipsw swift-
dump
• no public
• no
gener
a
tiveExperienceEssenti
a
lResource
sSt
a
tus()
• inst
a
nce v
a
ri
a
ble setters
a
nd getters
a
re stripped.
21
CatalogClient by Ghidra
• Yes, there is
C
a
t
a
logClient.gener
a
tiveExperienceEss
enti
a
lResourcesSt
a
tus()
• And we c
a
n get h
a
rd to re
a
d
decompiled code.
22
calling a CatalogClient instance method
• we c
a
n
f
ind th
a
t there is
• disp
a
tch thunk of ModelC
a
t
a
log.C
a
t
a
logClient.enoughStor
a
geForGener
a
tiveExperiencesEssenti
a
lResources()
a
sync throws -> Swift.Bool
• c
a
n we cre
a
te
a
nd inst
a
nce
a
nd c
a
ll it? Yes, it’s quite trivi
a
l. Add decl
a
r
a
tions of this function
a
nd the constructor then we c
a
n compile it.
• note th
a
t
a
thunk in Swift is
a
function or closure th
a
t del
a
ys the execution of
a
piece of code.
23
CatalogClient.generativeExperienceEssen
tialResourcesStatus() decompiled by
• de-compiled code gener
a
ted by LLMs
might not be
a
ccur
a
te, but it’s much
e
a
sier to re
a
d.
$ ipsw dyld disass 23A5308g__iPhone17,1/
dyld_shared_cache_arm64e --vaddr
0x18dfdcbe4 --dec --dec-model "Gemini 2.5
Pro (Preview)"
24
calling a CatalogClient instance method (cont’d)
• We c
a
n compile the following simple code successfully. However it f
a
iled to run.
• With `log stre
a
m --debug`, we c
a
n
f
ind some clues.
xxxxxxxxxxxx+0800 0x1f7c3f3 Error 0x0 538 0 modelcatalogd: (ModelCatalogRuntime) ModelCatalog com.apple.modelcatalog.full-access: Rejecting connection from 67927: lacking entitlement
• It seems we h
a
ve to
a
dd this “non-st
a
nd
a
rd” “com.
a
pple.modelc
a
t
a
log.full-
a
ccess” entitlement. Th
a
t is, it won’t work on iPhone
a
nd
m
a
c without Apple Mobile File Integrity (AMFI) dis
a
bled.
• Yes,
a
fter signing the bin
a
ry with the entitlement, it works.
import ModelCatalog
print("CatalogClient readiness:", CatalogClient.generativeExperienceEssentialResourcesStatus())
let client = CatalogClient()
print(client)
if let ready = try? await client.enoughStorageForGenerativeExperiencesEssentialResources() {
print(ready)
} else {
print("failed")
}
25
more CatalogClient methods
• with inst
a
nce methods resources()
a
nd resourceBundels() of the C
a
t
a
logClient cl
a
ss,
we c
a
n dump
a
lot of resource inform
a
tion
• remember to codesign with the “com.
a
pple.modelc
a
t
a
log.full-
a
ccess”
entitlement.
• Most of the inform
a
tion we see from modelc
a
t
a
logdump is in resources()
a
nd
a
nd
resourceBundles() outputs
import ModelCatalog
let client = CatalogClient()
let resources = try? client.resources()
print("resources:")
dump(resources!, indent: 1)
let bundles = try? client.resourceBundles()
print("resource bundles:")
dump(bundles!, indent: 1)
26
resourceBundles
27
there
a
re some c
a
ve
a
ts
a
nd limit
a
tions
Objective-C runtime API works for non-NSObject subclasses to some extent
• Underlying Object Model: Even when written without explicitly inheriting from NSObject, every Swift cl
a
ss on Apple pl
a
tforms still executes within
a
nd uses the Objective-C object model. This me
a
ns th
a
t
a
t
a
fund
a
ment
a
l level, they
a
re Objective-C objects. The root cl
a
ss for pure Swift cl
a
sses is
a
n intern
a
l
type often referred to
a
s _SwiftObject, which itself lever
a
ges the Objective-C runtime. The Objective-C runtime libr
a
ry provides support for the dyn
a
mic properties of the Objective-C l
a
ngu
a
ge
a
nd is linked by
a
ll Objective-C
a
pps.
• Cl
a
ss-Level APIs (Discovery
a
nd Introspection):
• Functions like objc_lookUpCl
a
ss(_:)
a
nd objc_getCl
a
ssList(_:_:) c
a
n indeed
f
ind
a
nd return Cl
a
ss objects for pure Swift cl
a
sses. This is bec
a
use these cl
a
sses
a
re registered with the Objective-C runtime.
• You c
a
n
a
lso use functions like cl
a
ss_getN
a
me(_:)
a
nd cl
a
ss_getSupercl
a
ss(_:) on these Swift cl
a
sses.
• Inst
a
nce-Level APIs (Cre
a
tion
a
nd M
a
nipul
a
tion):
• cl
a
ss_cre
a
teInst
a
nce(_:_:): You c
a
n use cl
a
ss_cre
a
teInst
a
nce(_:_:) to cre
a
te inst
a
nces of pure Swift cl
a
sses,
a
s they
a
re built upon the Objective-C object model.
• object_setIv
a
r(_:_:_:)
a
nd cl
a
ss_getInst
a
nceV
a
ri
a
ble(_:_:): This is where the limit
a
tions become more pronounced
a
nd d
a
ngerous.
• While these functions oper
a
te on Objective-C inst
a
nce v
a
ri
a
bles, directly m
a
nipul
a
ting properties of pure Swift cl
a
sses (especi
a
lly Swift v
a
lue types like String, Int, or structs th
a
t
a
re not explicitly exposed to Objective-C with @objc) vi
a
object_setIv
a
r() is gener
a
lly not recommended
a
nd highly
problem
a
tic.
• The intern
a
l memory l
a
yout (Applic
a
tion Bin
a
ry Interf
a
ce or ABI) of Swift cl
a
sses
a
nd their properties, p
a
rticul
a
rly for v
a
lue types, is not st
a
ble or publicly documented for direct m
a
nipul
a
tion. Relying on such intern
a
l det
a
ils c
a
n le
a
d to cr
a
shes, memory corruption, or unde
f
ined beh
a
vior with future
Swift upd
a
tes.
• For object_setIv
a
r() to work reli
a
bly, the property typic
a
lly needs to be
a
n Objective-C object type (like NSString for
a
String property)
a
nd often explicitly m
a
rked with @objc in the Swift cl
a
ss decl
a
r
a
tion to ensure proper bridging
a
nd runtime visibility. If the property is
a
true Swift v
a
lue type
a
nd
not bridged, object_setIv
a
r would expect
a
r
a
w pointer to the v
a
lue's stor
a
ge, which is extremely complex
a
nd uns
a
fe to m
a
n
a
ge m
a
nu
a
lly.
• The other w
a
y is to use Uns
a
fePointer or Uns
a
feR
a
wPointer.
• Method Invoc
a
tion (performSelector): To dyn
a
mic
a
lly c
a
ll methods on
a
Swift cl
a
ss inst
a
nce using Objective-C runtime functions like performSelector, the methods must be explicitly exposed to the Objective-C runtime using the @objc
a
ttribute.
• Key-V
a
lue Coding (KVC): KVC,
a
higher-level Objective-C mech
a
nism for dyn
a
mic property
a
ccess, requires the Swift cl
a
ss to inherit from NSObject
a
nd its properties to be m
a
rked with @objc. Therefore, KVC is gener
a
lly not directly
a
pplic
a
ble to pure Swift cl
a
sses th
a
t do not derive from NSObject.
• Swift Re
f
lection (Mirror API): It's import
a
nt to note th
a
t Swift's Mirror API is
a
Swift-n
a
tive re
f
lection mech
a
nism. It works with
a
ll Swift types (including pure Swift cl
a
sses
a
nd structs) for introspection (re
a
ding property n
a
mes
a
nd v
a
lues). However, it does not support direct modi
f
ic
a
tion of property v
a
lues..
• In summ
a
ry, while b
a
sic introspection of pure Swift cl
a
sses is possible through the Objective-C runtime, direct m
a
nipul
a
tion of their properties (especi
a
lly v
a
lue types) using low-level runtime functions like object_setIv
a
r() is highly discour
a
ged due to ABI inst
a
bility, complex memory m
a
n
a
gement,
a
nd the l
a
ck
of type s
a
fety. For dyn
a
mic property
a
ccess, it is gener
a
lly s
a
fer
a
nd more reli
a
ble to ensure your Swift cl
a
sses inherit from NSObject
a
nd expose relev
a
nt properties with @objc, then use higher-level mech
a
nisms like Key-V
a
lue Coding (KVC) when
a
ppropri
a
te.
28
objc_getClassList(_:_:_) in Swift
objc_getClassList(_:_:) in Swift
The objc_getClassList() is to obtain the list of registered class de
fi
nitions. Apple's documentation provides sample code on how to use it. However, it's in Objective-C even for the Swift version.
29
objc_getClassList(_:_:_) in Swift (cont’d)
• With th
a
t, we c
a
n
f
ind for the v
a
nill
a
dumpAllCl
a
sses (without
a
dding extr
a
fr
a
mework with -fr
a
mework ...), there
a
re
9, 680 cl
a
sses
a
nd 9,102 of them
a
re
a
re
NSObject derived.
• With -fr
a
mework Found
a
tionModels,
there
a
re more cl
a
sses (26,773
a
nd
25,470 of them
a
re NSObject derived).
30
import Foundation
import ObjectiveC
func isDerivedFromNSObject(_ aClass: AnyClass) -> Bool {
if aClass == NSObject.self {
return true
}
var superclass: AnyClass? = class_getSuperclass(aClass)
while let currentSuperclass = superclass {
if currentSuperclass == NSObject.self {
return true
}
superclass = class_getSuperclass(currentSuperclass)
}
return false
}
var numClasses = objc_getClassList(nil, 0)
if (numClasses > 0) {
var classes: UnsafeMutablePointer<AnyClass>? = nil
classes = UnsafeMutablePointer<AnyClass>.allocate(capacity: Int(numClasses))
numClasses = objc_getClassList(AutoreleasingUnsafeMutablePointer<AnyClass>(classes!), numClasses)
for i in 0..<Int(numClasses) {
// dump(classes![i], indent: 1)
if (isDerivedFromNSObject(classes![i])) {
print("name =", String(cString: class_getName(classes![i])), "is NSObject derived");
} else {
print("name =", String(cString: class_getName(classes![i])));
}
}
} else {
print("no classes found")
}
getting a instance variable
•
a
s we s
a
w, C
a
t
a
logClient cl
a
ss h
a
s some inst
a
nce
v
a
ri
a
bles. c
a
n we get them with something
like .us
a
geAli
a
sV
a
luesForUs
a
geAli
a
s?
• Unfortun
a
tely, it seems not.
• How
a
bout other w
a
ys?
• KVC: The ModelC
a
t
a
log.C
a
t
a
logClient is not
NSObject derived.
• mirror: yes, dump(client) works. other Mirror
methods works. But remember th
a
t Mirror is re
a
d
only
• Objective-C runtime APIs. Yes, it works to some
extent.
31
import Foundation
import ModelCatalog
print("status: ", CatalogClient.generativeExperienceEssentialResourcesStatus())
let client = CatalogClient()
print(client)
print(client.usageAliasValuesForUsageAlias)
if let ready = try? await client.enoughStorageForGenerativeExperiencesEssentialResources()
print(ready)
} else {
print("failed")
}
getting a instance variable (cont’d)
• With cl
a
ss_getInst
a
nceV
a
ri
a
ble(_:_:)
a
nd
object_getIv
a
r(_:_:), we get the
client.us
a
geAli
a
sV
a
luesForUs
a
geAli
a
s
• How
a
bout setting
a
n inst
a
nce
v
a
ri
a
ble? It’s
a
bit tricky.
32
import Foundation
import ModelCatalog
print("status: ", CatalogClient.generativeExperienceEssentialResourcesStatus())
let client = CatalogClient()
print(client)
let usage = class_getInstanceVariable(CatalogClient.self, "usageAliasValuesForUsageAlias")
print(object_getIvar(client, usage!)!)
Sometime we need to set non-NSObject instance variables
• st
a
rting from iOS/m
a
cOS 26, developers could
a
ccess AFM on-device with APIs in
Found
a
tionModels.fr
a
mework.
import FoundationModels
let session = LanguageModelSession()
let results = try? await session.respond(to: "tell me something
about systolic array")
print(results!)
• Apple’s document
a
tion tells two sessions properties.
• Inspecting the the session, e.g.,
a
dding
dump(session, m
a
xDepth: 2)
a
fter got session, we
know there
a
re more. The
Genener
a
tiveModelInferenceSession is
a
non-
NSObject cl
a
ss. If we w
a
nt to construct it ourselves,
we need to set non-NSObject inst
a
nce v
a
ri
a
bles.
33
class GenerativeModelInferenceSession {
var generator: TokenGeneration.TokenGenerator
var guardrails: FoundationModels.LanguageModelSession.Guardrails
var stringRenderedPromptSanitizer: GenerativeModels.StringRenderedPromptSanitizer
var stringResponseSanitizer: GenerativeModels.StringResponseSanitizer
var modelBundleID: Swift.String
var useCase: FoundationModels.SystemLanguageModel.UseCase
var sessionID: Swift.String
}
a tiny example for a String
instance variable
• String != NSString, there
a
re
a
uto-bridging mech
a
nism,
but it’s not wh
a
t we w
a
nt. E.g., the
iv
a
r_getTypeEncoding(_:) won’t return “@“ (id, object) for
Swift String
• object_lookUpCl
a
ss()
a
nd cl
a
ss_cre
a
teInst
a
nce() work for
both NSObject
a
nd non-NSObject cl
a
sses.
• object_getIv
a
r() should not be used.
• KVC only works NSObject.
• wh
a
t re
a
lly works
• ObjectIdenti
f
ier() to get
a
n object’s
a
ddress.
• Uns
a
feMut
a
blePointer/Uns
a
feMut
a
bleR
a
wPointer to
ch
a
nge the v
a
lue of
a
pointee.
34
import Foundation
@objc
class Test: NSObject {
@objc var modelBundleID: String = "original"
override init() {
modelBundleID = "init"
}
}
let t = Test()
print("t:")
dump(t, indent: 1)
let tClass = objc_lookUpClass("test_string_in_class.Test")
var t2 = class_createInstance(tClass, 0)
print("t2:")
dump(t2!, indent: 1)
var mbi = class_getInstanceVariable(tClass, "modelBundleID")
print("mbi: (mbi!)")
// print("modelBundleID: (object_getIvar(t!, mbi!))") will cause segmentation fault
// print("modelBundleID: (object_getIvar(t2!, mbi!))”) ditto
var bi = "com.apple.fm.language.instruct_3b.fm_api_generic"
// object_setIvar(t2!, mbi!, bi) works sometimes
// print("after setIvar modelBundleID: (object_getIvar(t2!, mbi!))") works sometimes
// dump(t2!, indent: 1) will cause segmentation fault
(t2 as! NSObject).setValue("Updated via KVC!", forKey: "modelBundleID")
dump(t2!, indent: 1)
// KVC works for NSObject-derived classes
// Define the print(address:as:) function
func print<T>(address p: UnsafeRawPointer, as type: T.Type) {
let value = p.load(as: type)
print(value)
dump(value, indent: 1)
print("p: (p), type (type)")
}
let t2addr = unsafeBitCast(ObjectIdentifier(t2 as AnyObject), to: Int.self)
print("t2addr: ", String(format: "0x%lx", t2addr))
let offset = ivar_getOffset(mbi!)
print("moduleBundleID offset: ", offset)
// get a pointer to String instance variable
let p = UnsafeMutableRawPointer(bitPattern: t2addr + offset)
print(address: p!, as: String.self)
// get a type pointer
let typedPtr = p!.bindMemory(to: String.self, capacity: 1)
print("pointee:", typedPtr.pointee)
typedPtr.pointee = "test setting with poiter"
print("pointee:", typedPtr.pointee)
print("t2:")
dump(t2!, indent: 1)
Swift Runtime API
• Yes, there
a
re some swift_* functions
in /usr/lib/swift/libswiftCore.dylib (in
dyld_sh
a
red_c
a
che).
• source code is
a
v
a
il
a
ble
• most of them h
a
ve C he
a
ders
somewhere in the source code
• However, it seems they
a
re not
a
s
complete
a
s Objective-C. And I could
not
f
ind one to set inst
a
nce v
a
ri
a
bles
35
wrap-up
• How to use Swift API in priv
a
te fr
a
meworks or non-public Swift API in public fr
a
meworks
• either
a
dd Module/, including .swiftinterf
a
ce
f
iles, or
• Objective-C runtime API + Uns
a
fePointer/Uns
a
feR
a
wPointer
• Tools:
• bin
a
ry utils (otool, nm, etc.), swift toolch
a
in (including swift dem
a
ngle), Ghidr
a
, ipse
36

Learning Swift: A Stupid Way -- How to use Swift API in private frameworks

  • 1.
    Koan-Sin Tan, freedom@computer.org,Aug 10th, 2025, COSCUP 2025 Learning Swift: A Stupid Way 1
  • 2.
    • feel freeto interrupt me a nytime 2
  • 3.
    About me • Le a rntto use open-source softw a re before the term “open source” w a s coined on VAX-11/780. • Le a rnt some Sm a llT a lk-80 in e a rly 1990s. • Le a rnt some Objective-C progr a mming on NeXT m a chines. • W a s excited to he a r “Swift is the Objective-C without the C”. Thought it’s more Sm a llT a lk-80 like. You know it’s not. • Know very little Swift 3 http://gunkies.org/w/images/c/c1/DEC-VAX-11-780.jpg
  • 4.
    using Swift APIfrom PrivateFrameworks • Some new Apple fr a meworks a re Swift only, either public or priv a te fr a meworks • CoreML: Objective-C a nd Swift • Found a tionModels, ModelC a t a log: Swift only 4
  • 5.
    Why using SwiftAPIs in Apple’s private frameworks is an issue? • Although there m a ny “public symbols” we c a n use / link, there a re no decl a r a tions. • For Objective-C, there a re sever a l cl a ss-dump tools a v a il a ble on the internet. • Find inform a tion from a compiled M a ch-O f ile, the execut a ble form a t used by m a cOS, iOS, a nd other Apple oper a ting systems, by a n a lyzing the Objective-C runtime inform a tion embedded in it a nd gener a tes corresponding Objective-C he a der f iles. • There a re no tools a re good a s cl a ss-dump for dump Swift API to .swiftinterf a ce, which we’ll expl a in l a ter. • If you’re f a mili a r with the Objective-C runtime API, you might wonder if we c a n use it to dyn a mic a lly a ccess construct Swift objects. Altern a tively, is there a corresponding “Swift runtime API”? 5
  • 6.
    public vs. privateframeworks • public fr a meworks, such a s CoreML, come with C/C++/Objective-C he a ders in He a ders a nd Swift decl a r a tions in Modules. • priv a te fr a meworks h a ve none of them • if you don’t know .tbd, it’s Apple’s (text- b a sed dyn a mic libr a ry stu ff ). While there isn't one single "o ff ici a l speci f ic a tion document" re a dily a v a il a ble to the public, there a re open-source implement a tions in LLVM. 6
  • 7.
    swiftinterface • swiftinterf a ce f iles a re a key componentof Swift's Module St a bility a nd Libr a ry Evolution fe a tures. They a re a textu a l represent a tion of a Swift module's public API, gener a ted by the Swift compiler during the build process from the origin a l Swift source code. • The .swiftinterf a ce f ile cont a ins: • Decl a r a tions: The n a mes of a ll public types, functions, a nd v a ri a bles. • Type Inform a tion: The sign a tures of these public members, including their return types, p a r a meter types, a nd a ny generic constr a ints. • Module-Speci f ic Inform a tion: Det a ils th a t a llow the compiler to link a g a inst the module correctly. • This f ile is essenti a lly a "st a ble ABI he a der" for Swift modules, ensuring th a t a libr a ry compiled with one version of the Swift compiler c a n be used by a n a pplic a tion compiled with a di ff erent, comp a tible version of the compiler. • When a Swift bin a ry is compiled into a M a ch-O f ile, the origin a l source code a nd the det a iled type inform a tion in the .swiftinterf a ce f ile a re not preserved. While the M a ch-O bin a ry cont a ins a signi f ic a nt a mount of symbol a nd met a d a t a , it is not enough to reverse- engineer the complete a nd a ccur a te Swift-level API. 7
  • 8.
    C/C++/Objective-C/Swift symbols oniOS/macOS • C: no function sign a tures, no nothing • C++: function p a r a meters type in m a ngled symbols, vt a ble, etc. The return types a re not in m a ngled n a mes. • Objective-C: dyn a mic type (generic object id, dyn a mic binding, etc.) so there a re m a ny inform a tion. cl a ss-dump somewh a t showed wh a t we c a n from bin a ry f iles. • Swift: Swift n a me m a ngling spec a nd “swift dem a ngle” a re quite comprehensive. 8
  • 9.
  • 10.
    Using ModelCatalog.framework • Withthe help of otool a nd lldb, we’ve discovered th a t the modelc a tlogdump utilizes the priv a te fr a mework of the ModelC a t a log.fr a mework. • The ModelC a t a log fr a mework o ff ers very limited Objective-C APIs. • Interestingly, the object cl a sses _TtC…. a re a ctu a lly Swift cl a sses. • The _OBJC_CLASS_$_MCResourceInform a tion a nd _OBJC_CLASS_$_MCResourceSt a tus cl a sses don’t provide a ny useful inform a tion. We’ll revisit this l a ter. • Therefore, we need to ex a mine the Swift API. • Let’s check if we do “import ModelC a t a log” in Swift 10
  • 11.
  • 12.
  • 13.
    13 LLM a nd Di ff usion rel a tedcl a sses in symbol t a ble
  • 14.
    import ModelCatalog Let's checkif we can make import ModelCatalog in Swift work. Let's use stu ff in /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks By default, it doesn't work $ swiftc test_import_mc_1.swift -F /Library/Developer/CommandLineTools/SDKs/ MacOSX.sdk/System/Library/PrivateFrameworks test_import_mc_1.swift:1:8: error: no such module 'ModelCatalog 1 | import ModelCatalog | `- error: no such module 'ModelCatalog' 2 | $ 14
  • 15.
    import ModelCatalog (cont’d) •It seems module.modulesm a p is needed • it seems there is not a single, comprehensive "o ff ici a l" Apple document a tion for the module.modulem a p f ile form a t. We f ind inform a tion in llvm/cl a ng document a tions a nd source code. • Since we a re le a rning in a stupid w a y. Let’s use copy- a nd-p a ste a nd tri a l- a nd-error. I copied module.modulemap from CoreML.framework and change CoreML to ModelCatalog, framework module ModelCatalog { umbrella header "ModelCatalog.h" export * module * { export * } } The ModelCatalog.h could be an empty one. That is, we could create an empty /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/ModelCatalog.framework/Headers/ModelCatalog.h then we can compile the one line ("import ModelCatalog") program successfully. 15
  • 16.
    import ModelCatalog (cont’d) Canwe ingore the umbrella header "ModelCatalog.h" line, it seems this is for Objective-C to Swift bridge (for Swift code to use Objective-C binary code). YES, if we remove the line only, we got messages like $ swiftc test_import_mc_1.swift -F /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/ModelCatalog.framework/Modules/ module.modulemap:4:12: error: inferred submodules require a module with an umbrella 2 | 3 | export * 4 | module * { export * } | `- error: inferred submodules require a module with an umbrella 5 | } 6 | It seems line 4 here 4 | module * { export * } is also for using Objective-C. When we remove it too, it WORKS. That is framework module ModelCatalog { export * } is enough. 16
  • 17.
    As we know,/usr/bin/modelcatalogdump could be a good starting point. By setting breakpoints with b -r ModelCatalog in lldb modelcatalogdump, the fi rst method I captured was ModelCatalog`static ModelCatalog.CatalogClient.generativeExperienceEssentialResourcesStatus() -> ModelCatalog.ResourceReadinessStatus By examining exported symbols with nm and swift demangle, we know that ModelCatalog.CatalogClient is a class in the ModelCatalog framework and static ModelCatalog.CatalogClient.generativeExperienceEssentialResourcesStatus() -> ModelCatalog.ResourceReadinessStatus is a class method of CatalogClient. It takes no argument and returns an instance of ModelCatalog.ResourceReadinessStatus, which is a Swift enum. 17
  • 18.
    18 as /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/ModelCatalog.framework/Modules/ ModelCatalog.swiftmodule/arm64e-apple-macos.swiftinterface we can compileand run the following simple swift code import ModelCatalog print("CatalogClient readiness:", CatalogClient.generativeExperienceEssentialResourcesStatus() That is, we fi gured out how to call undocumented Swift API in /System/Library/PrivateFrameworks/. Note that the fi rst 3 lines starting with // are necessary. I copied them from CoreML.framework and change CoreML to ModelCatalog.
  • 19.
  • 20.
    Tools • So f a r,we use nm, otool, lldb, a nd swift dem a ngle. Are there a ny other convenient open-source tools. • Yes, to me, a t le a st Ghidr a a nd ipsw a re quite useful. • Ghidr a is a n open-source reversing tool from NSA. It could • a n a lyze dyld_sh a red_c a che, dissemble functions, decompile m a chine code into C/C++ like functions. • ipsw is kind a “iOS/m a cOS Rese a rch Swiss Army Knife”, I used it to • downlo a d Apple .ipsw f iles, extr a ct a nd re a d dyld_sh a red_c a che from Apple .ipsw f ile, a nd decompile some functions with Github Copilot • ipsw a lso provides Objective-C cl a ss-dump a nd swift-dump • Unfortun a tely, swift-dump is not complete. E.g,, it could not gener a te .sw f itinterf a ce f iles a nd didn’t h a ndle generic type well. 20
  • 21.
    CatalogClient by ipswswift- dump • no public • no gener a tiveExperienceEssenti a lResource sSt a tus() • inst a nce v a ri a ble setters a nd getters a re stripped. 21
  • 22.
    CatalogClient by Ghidra •Yes, there is C a t a logClient.gener a tiveExperienceEss enti a lResourcesSt a tus() • And we c a n get h a rd to re a d decompiled code. 22
  • 23.
    calling a CatalogClientinstance method • we c a n f ind th a t there is • disp a tch thunk of ModelC a t a log.C a t a logClient.enoughStor a geForGener a tiveExperiencesEssenti a lResources() a sync throws -> Swift.Bool • c a n we cre a te a nd inst a nce a nd c a ll it? Yes, it’s quite trivi a l. Add decl a r a tions of this function a nd the constructor then we c a n compile it. • note th a t a thunk in Swift is a function or closure th a t del a ys the execution of a piece of code. 23
  • 24.
    CatalogClient.generativeExperienceEssen tialResourcesStatus() decompiled by •de-compiled code gener a ted by LLMs might not be a ccur a te, but it’s much e a sier to re a d. $ ipsw dyld disass 23A5308g__iPhone17,1/ dyld_shared_cache_arm64e --vaddr 0x18dfdcbe4 --dec --dec-model "Gemini 2.5 Pro (Preview)" 24
  • 25.
    calling a CatalogClientinstance method (cont’d) • We c a n compile the following simple code successfully. However it f a iled to run. • With `log stre a m --debug`, we c a n f ind some clues. xxxxxxxxxxxx+0800 0x1f7c3f3 Error 0x0 538 0 modelcatalogd: (ModelCatalogRuntime) ModelCatalog com.apple.modelcatalog.full-access: Rejecting connection from 67927: lacking entitlement • It seems we h a ve to a dd this “non-st a nd a rd” “com. a pple.modelc a t a log.full- a ccess” entitlement. Th a t is, it won’t work on iPhone a nd m a c without Apple Mobile File Integrity (AMFI) dis a bled. • Yes, a fter signing the bin a ry with the entitlement, it works. import ModelCatalog print("CatalogClient readiness:", CatalogClient.generativeExperienceEssentialResourcesStatus()) let client = CatalogClient() print(client) if let ready = try? await client.enoughStorageForGenerativeExperiencesEssentialResources() { print(ready) } else { print("failed") } 25
  • 26.
    more CatalogClient methods •with inst a nce methods resources() a nd resourceBundels() of the C a t a logClient cl a ss, we c a n dump a lot of resource inform a tion • remember to codesign with the “com. a pple.modelc a t a log.full- a ccess” entitlement. • Most of the inform a tion we see from modelc a t a logdump is in resources() a nd a nd resourceBundles() outputs import ModelCatalog let client = CatalogClient() let resources = try? client.resources() print("resources:") dump(resources!, indent: 1) let bundles = try? client.resourceBundles() print("resource bundles:") dump(bundles!, indent: 1) 26
  • 27.
  • 28.
    there a re some c a ve a ts a ndlimit a tions Objective-C runtime API works for non-NSObject subclasses to some extent • Underlying Object Model: Even when written without explicitly inheriting from NSObject, every Swift cl a ss on Apple pl a tforms still executes within a nd uses the Objective-C object model. This me a ns th a t a t a fund a ment a l level, they a re Objective-C objects. The root cl a ss for pure Swift cl a sses is a n intern a l type often referred to a s _SwiftObject, which itself lever a ges the Objective-C runtime. The Objective-C runtime libr a ry provides support for the dyn a mic properties of the Objective-C l a ngu a ge a nd is linked by a ll Objective-C a pps. • Cl a ss-Level APIs (Discovery a nd Introspection): • Functions like objc_lookUpCl a ss(_:) a nd objc_getCl a ssList(_:_:) c a n indeed f ind a nd return Cl a ss objects for pure Swift cl a sses. This is bec a use these cl a sses a re registered with the Objective-C runtime. • You c a n a lso use functions like cl a ss_getN a me(_:) a nd cl a ss_getSupercl a ss(_:) on these Swift cl a sses. • Inst a nce-Level APIs (Cre a tion a nd M a nipul a tion): • cl a ss_cre a teInst a nce(_:_:): You c a n use cl a ss_cre a teInst a nce(_:_:) to cre a te inst a nces of pure Swift cl a sses, a s they a re built upon the Objective-C object model. • object_setIv a r(_:_:_:) a nd cl a ss_getInst a nceV a ri a ble(_:_:): This is where the limit a tions become more pronounced a nd d a ngerous. • While these functions oper a te on Objective-C inst a nce v a ri a bles, directly m a nipul a ting properties of pure Swift cl a sses (especi a lly Swift v a lue types like String, Int, or structs th a t a re not explicitly exposed to Objective-C with @objc) vi a object_setIv a r() is gener a lly not recommended a nd highly problem a tic. • The intern a l memory l a yout (Applic a tion Bin a ry Interf a ce or ABI) of Swift cl a sses a nd their properties, p a rticul a rly for v a lue types, is not st a ble or publicly documented for direct m a nipul a tion. Relying on such intern a l det a ils c a n le a d to cr a shes, memory corruption, or unde f ined beh a vior with future Swift upd a tes. • For object_setIv a r() to work reli a bly, the property typic a lly needs to be a n Objective-C object type (like NSString for a String property) a nd often explicitly m a rked with @objc in the Swift cl a ss decl a r a tion to ensure proper bridging a nd runtime visibility. If the property is a true Swift v a lue type a nd not bridged, object_setIv a r would expect a r a w pointer to the v a lue's stor a ge, which is extremely complex a nd uns a fe to m a n a ge m a nu a lly. • The other w a y is to use Uns a fePointer or Uns a feR a wPointer. • Method Invoc a tion (performSelector): To dyn a mic a lly c a ll methods on a Swift cl a ss inst a nce using Objective-C runtime functions like performSelector, the methods must be explicitly exposed to the Objective-C runtime using the @objc a ttribute. • Key-V a lue Coding (KVC): KVC, a higher-level Objective-C mech a nism for dyn a mic property a ccess, requires the Swift cl a ss to inherit from NSObject a nd its properties to be m a rked with @objc. Therefore, KVC is gener a lly not directly a pplic a ble to pure Swift cl a sses th a t do not derive from NSObject. • Swift Re f lection (Mirror API): It's import a nt to note th a t Swift's Mirror API is a Swift-n a tive re f lection mech a nism. It works with a ll Swift types (including pure Swift cl a sses a nd structs) for introspection (re a ding property n a mes a nd v a lues). However, it does not support direct modi f ic a tion of property v a lues.. • In summ a ry, while b a sic introspection of pure Swift cl a sses is possible through the Objective-C runtime, direct m a nipul a tion of their properties (especi a lly v a lue types) using low-level runtime functions like object_setIv a r() is highly discour a ged due to ABI inst a bility, complex memory m a n a gement, a nd the l a ck of type s a fety. For dyn a mic property a ccess, it is gener a lly s a fer a nd more reli a ble to ensure your Swift cl a sses inherit from NSObject a nd expose relev a nt properties with @objc, then use higher-level mech a nisms like Key-V a lue Coding (KVC) when a ppropri a te. 28
  • 29.
    objc_getClassList(_:_:_) in Swift objc_getClassList(_:_:)in Swift The objc_getClassList() is to obtain the list of registered class de fi nitions. Apple's documentation provides sample code on how to use it. However, it's in Objective-C even for the Swift version. 29
  • 30.
    objc_getClassList(_:_:_) in Swift(cont’d) • With th a t, we c a n f ind for the v a nill a dumpAllCl a sses (without a dding extr a fr a mework with -fr a mework ...), there a re 9, 680 cl a sses a nd 9,102 of them a re a re NSObject derived. • With -fr a mework Found a tionModels, there a re more cl a sses (26,773 a nd 25,470 of them a re NSObject derived). 30 import Foundation import ObjectiveC func isDerivedFromNSObject(_ aClass: AnyClass) -> Bool { if aClass == NSObject.self { return true } var superclass: AnyClass? = class_getSuperclass(aClass) while let currentSuperclass = superclass { if currentSuperclass == NSObject.self { return true } superclass = class_getSuperclass(currentSuperclass) } return false } var numClasses = objc_getClassList(nil, 0) if (numClasses > 0) { var classes: UnsafeMutablePointer<AnyClass>? = nil classes = UnsafeMutablePointer<AnyClass>.allocate(capacity: Int(numClasses)) numClasses = objc_getClassList(AutoreleasingUnsafeMutablePointer<AnyClass>(classes!), numClasses) for i in 0..<Int(numClasses) { // dump(classes![i], indent: 1) if (isDerivedFromNSObject(classes![i])) { print("name =", String(cString: class_getName(classes![i])), "is NSObject derived"); } else { print("name =", String(cString: class_getName(classes![i]))); } } } else { print("no classes found") }
  • 31.
    getting a instancevariable • a s we s a w, C a t a logClient cl a ss h a s some inst a nce v a ri a bles. c a n we get them with something like .us a geAli a sV a luesForUs a geAli a s? • Unfortun a tely, it seems not. • How a bout other w a ys? • KVC: The ModelC a t a log.C a t a logClient is not NSObject derived. • mirror: yes, dump(client) works. other Mirror methods works. But remember th a t Mirror is re a d only • Objective-C runtime APIs. Yes, it works to some extent. 31 import Foundation import ModelCatalog print("status: ", CatalogClient.generativeExperienceEssentialResourcesStatus()) let client = CatalogClient() print(client) print(client.usageAliasValuesForUsageAlias) if let ready = try? await client.enoughStorageForGenerativeExperiencesEssentialResources() print(ready) } else { print("failed") }
  • 32.
    getting a instancevariable (cont’d) • With cl a ss_getInst a nceV a ri a ble(_:_:) a nd object_getIv a r(_:_:), we get the client.us a geAli a sV a luesForUs a geAli a s • How a bout setting a n inst a nce v a ri a ble? It’s a bit tricky. 32 import Foundation import ModelCatalog print("status: ", CatalogClient.generativeExperienceEssentialResourcesStatus()) let client = CatalogClient() print(client) let usage = class_getInstanceVariable(CatalogClient.self, "usageAliasValuesForUsageAlias") print(object_getIvar(client, usage!)!)
  • 33.
    Sometime we needto set non-NSObject instance variables • st a rting from iOS/m a cOS 26, developers could a ccess AFM on-device with APIs in Found a tionModels.fr a mework. import FoundationModels let session = LanguageModelSession() let results = try? await session.respond(to: "tell me something about systolic array") print(results!) • Apple’s document a tion tells two sessions properties. • Inspecting the the session, e.g., a dding dump(session, m a xDepth: 2) a fter got session, we know there a re more. The Genener a tiveModelInferenceSession is a non- NSObject cl a ss. If we w a nt to construct it ourselves, we need to set non-NSObject inst a nce v a ri a bles. 33 class GenerativeModelInferenceSession { var generator: TokenGeneration.TokenGenerator var guardrails: FoundationModels.LanguageModelSession.Guardrails var stringRenderedPromptSanitizer: GenerativeModels.StringRenderedPromptSanitizer var stringResponseSanitizer: GenerativeModels.StringResponseSanitizer var modelBundleID: Swift.String var useCase: FoundationModels.SystemLanguageModel.UseCase var sessionID: Swift.String }
  • 34.
    a tiny examplefor a String instance variable • String != NSString, there a re a uto-bridging mech a nism, but it’s not wh a t we w a nt. E.g., the iv a r_getTypeEncoding(_:) won’t return “@“ (id, object) for Swift String • object_lookUpCl a ss() a nd cl a ss_cre a teInst a nce() work for both NSObject a nd non-NSObject cl a sses. • object_getIv a r() should not be used. • KVC only works NSObject. • wh a t re a lly works • ObjectIdenti f ier() to get a n object’s a ddress. • Uns a feMut a blePointer/Uns a feMut a bleR a wPointer to ch a nge the v a lue of a pointee. 34 import Foundation @objc class Test: NSObject { @objc var modelBundleID: String = "original" override init() { modelBundleID = "init" } } let t = Test() print("t:") dump(t, indent: 1) let tClass = objc_lookUpClass("test_string_in_class.Test") var t2 = class_createInstance(tClass, 0) print("t2:") dump(t2!, indent: 1) var mbi = class_getInstanceVariable(tClass, "modelBundleID") print("mbi: (mbi!)") // print("modelBundleID: (object_getIvar(t!, mbi!))") will cause segmentation fault // print("modelBundleID: (object_getIvar(t2!, mbi!))”) ditto var bi = "com.apple.fm.language.instruct_3b.fm_api_generic" // object_setIvar(t2!, mbi!, bi) works sometimes // print("after setIvar modelBundleID: (object_getIvar(t2!, mbi!))") works sometimes // dump(t2!, indent: 1) will cause segmentation fault (t2 as! NSObject).setValue("Updated via KVC!", forKey: "modelBundleID") dump(t2!, indent: 1) // KVC works for NSObject-derived classes // Define the print(address:as:) function func print<T>(address p: UnsafeRawPointer, as type: T.Type) { let value = p.load(as: type) print(value) dump(value, indent: 1) print("p: (p), type (type)") } let t2addr = unsafeBitCast(ObjectIdentifier(t2 as AnyObject), to: Int.self) print("t2addr: ", String(format: "0x%lx", t2addr)) let offset = ivar_getOffset(mbi!) print("moduleBundleID offset: ", offset) // get a pointer to String instance variable let p = UnsafeMutableRawPointer(bitPattern: t2addr + offset) print(address: p!, as: String.self) // get a type pointer let typedPtr = p!.bindMemory(to: String.self, capacity: 1) print("pointee:", typedPtr.pointee) typedPtr.pointee = "test setting with poiter" print("pointee:", typedPtr.pointee) print("t2:") dump(t2!, indent: 1)
  • 35.
    Swift Runtime API •Yes, there a re some swift_* functions in /usr/lib/swift/libswiftCore.dylib (in dyld_sh a red_c a che). • source code is a v a il a ble • most of them h a ve C he a ders somewhere in the source code • However, it seems they a re not a s complete a s Objective-C. And I could not f ind one to set inst a nce v a ri a bles 35
  • 36.
    wrap-up • How touse Swift API in priv a te fr a meworks or non-public Swift API in public fr a meworks • either a dd Module/, including .swiftinterf a ce f iles, or • Objective-C runtime API + Uns a fePointer/Uns a feR a wPointer • Tools: • bin a ry utils (otool, nm, etc.), swift toolch a in (including swift dem a ngle), Ghidr a , ipse 36