Object Incarnation in Swift
Having created a servant class such as the rudimentary NodeI class, you can instantiate the class to create a concrete servant that can receive invocations from a client. However, merely instantiating a servant class or struct is insufficient to incarnate an object. Specifically, to provide an implementation of an Ice object, you must take the following steps:
- Instantiate a servant class or struct.
- Create an identity for the Ice object incarnated by the servant.
- Inform the Ice run-time of the existence of the servant.
- Pass a proxy for the object to a client so the client can reach it.
On this page:
Instantiating a Swift Servant
Instantiating a servant means to create an instance of the servant class or struct:
let servant = NodeI("Fred")
Creating an Identity in Swift
Each Ice object requires an identity. That identity must be unique for all servants using the same object adapter.
The Ice object model assumes that all objects (regardless of their adapter) have a globally unique identity.
An Ice object identity is a structure with the following Slice definition:
module Ice { struct Identity { string name; string category; } // ... }
The full identity of an object is the combination of both the name
and category
fields of the Identity
structure. For now, we will leave the category
field as the empty string and simply use the name
field. (The category
field is most often used in conjunction with servant locators.)
To create an identity, we simply assign a key that identifies the servant to the name
field of the Identity
structure:
let id = Ice.Identity("Fred", "") // Not unique, but good enough for now
Activating a Swift Servant
Merely creating a servant instance does nothing: the Ice run time becomes aware of the existence of a servant only once you explicitly tell the object adapter about the servant. To activate a servant, you need to create request dispatcher that holds your servant and invoke the add
operation on the object adapter with this request dispatcher:
Assuming that we have access to the object adapter in the adapter
variable, we can write:
try adapter.add(servant: NodeDisp(servant), id: id)
Note the two arguments to add
: the request dispatcher and the object identity. Calling add
on the object adapter adds the request dispatcher (and indirectly the servant) and the servant's identity to the adapter's servant map and links the proxy for an Ice object to the correct servant instance in the server's memory as follows:
- The proxy for an Ice object, apart from addressing information, contains the identity of the Ice object. When a client invokes an operation, the object identity is sent with the request to the server.
- The object adapter receives the request, retrieves the identity, and uses the identity as an index into the servant map.
- If a servant with that identity is active, the object adapter retrieves the request dispatcher from the servant map and dispatches the incoming request into the correct method of the servant via its dispatcher.
Assuming that the object adapter is in the active state, client requests are dispatched to the servant as soon as you call add
.
Matching Request Dispatcher Structs
When you add
a request dispatcher to your object adapter, it is critical to create a request dispatcher instance that corresponds to your servant's most derived Slice interface. If you mistakenly create a request dispatcher for a base interface, clients won't be able to reach the operations of the most derived interface, and will receive Ice.OperationNotExistException
when attempting to call these operations.
UUIDs as Identities in Swift
The Ice object model assumes that object identities are globally unique. One way of ensuring that uniqueness is to use UUIDs (Universally Unique Identifiers) as identities, and for convenience, the object adapter provides an operation addWithUUID
that generates a UUID and adds a servant to the servant map in a single step.
Using this operation, we can create an identity and register a servant with that identity in a single step as follows:
try adapter.addWithUUID(servant: NodeDisp(NodeI("Fred")))
Creating Proxies in Swift
Once we have activated a servant for an Ice object, the server can process incoming client requests for that object. However, clients can only access the object once they hold a proxy for the object. If a client knows the server's address details and the object identity, it can create a proxy from a string, as we saw in our first example in Hello World Application. However, creation of proxies by the client in this manner is usually only done to allow the client access to initial objects for bootstrapping. Once the client has an initial proxy, it typically obtains further proxies by invoking operations.
The object adapter contains all the details that make up the information in a proxy: the object identity and the addressing information. The Ice run time offers a number of ways to create proxies. Once created, you can pass a proxy to the client as the return value or as an out-parameter of an operation invocation.
Proxies and Servant Activation in Swift
The add
and addWithUUID
servant activation operations on the object adapter return a proxy for the corresponding Ice object. This means we can write the following:
let proxy = uncheckedCast(prx: adapter.addWithUUID(servant: NodeDisp(NodeI("Fred"))), type: NodePrx.self)
Here, addWithUUID
both activates the servant and returns a proxy for the Ice object incarnated by that servant in a single step.
Note that we need to use an uncheckedCast
here because addWithUUID
returns a proxy of type ObjectPrx
.
Direct Proxy Creation in Swift
The object adapter offers an operation to create a proxy for a given identity:
module Ice { local interface ObjectAdapter { ["swift:nonnull"] Object* createProxy(Identity id); // ... } }
Note that createProxy
creates a proxy for a given identity whether a servant is activated with that identity or not. In other words, proxies have a life cycle that is quite independent of the life cycle of servants:
let id = Ice.Identity("Foo") let prx = try adapter.createProxy(id: id)
This creates a proxy for an Ice object with the identity "Foo"
. Obviously, no servant exists for that object so, if we return the proxy to a client and the client invokes an operation on the proxy, the client will receive an ObjectNotExistException
.