Swiftpack.co - Lakr233/NSRemoteShell as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by Lakr233.
Lakr233/NSRemoteShell pre-2.0
Remote shell using libssh2 with Objective-C, thread safe implementation.
⭐️ 12
🕓 15 weeks ago
.package(url: "https://github.com/Lakr233/NSRemoteShell.git", from: "pre-2.0")


Remote shell using libssh2 with Objective-C. Thread safe implementation. Available as Swift Package.


libssh2 prebuilt binaries are required to build this package. Either clone with recursive submodules or update after clone. Bitcode is available.

git submodule update --init --recursive --remote

See following options to learn more.


In our design, all operation is blocked, and is recommended to call in background thread.

    .setupConnectionPort(NSNumber(value: port))
    .authenticate(with: username, andPassword: password)
        withExecTimeout: .init(value: 0)
    ) {
    } withContinuationHandler: {
        commandStatus != .terminating

To connect, call setup function to set host and port. Class is designed with Swift function-like syntax chain.

- (instancetype)setupConnectionHost:(nonnull NSString *)targetHost;
- (instancetype)setupConnectionPort:(nonnull NSNumber *)targetPort;
- (instancetype)setupConnectionTimeout:(nonnull NSNumber *)timeout;

- (instancetype)requestConnectAndWait;
- (instancetype)requestDisconnectAndWait;

There is two authenticate method provided. Authenticate is required after connect.

Do not change username when authenticateing the same session.

- (instancetype)authenticateWith:(nonnull NSString *)username
                     andPassword:(nonnull NSString *)password;
- (instancetype)authenticateWith:(NSString *)username
                            andPublicKey:(nullable NSString *)publicKey
                            andPrivateKey:(NSString *)privateKey
                             andPassword:(nullable NSString *)password;

For various session property, see property list.

@property (nonatomic, readwrite, nullable, strong) NSString *resolvedRemoteIpAddress;
@property (nonatomic, readwrite, nullable, strong) NSString *remoteBanner;
@property (nonatomic, readwrite, nullable, strong) NSString *remoteFingerPrint;

@property (nonatomic, readwrite, getter=isConnected) BOOL connected;
@property (nonatomic, readwrite, getter=isAuthenticated) BOOL authenticated;

Request either command channel or shell channel with designated API, and do not access unexposed values. It may break the ARC or crash the app.

- (instancetype)executeRemote:(NSString*)command
                  withOutput:(nullable void (^)(NSString*))responseDataBlock
     withContinuationHandler:(nullable BOOL (^)(void))continuationBlock;

- (instancetype)openShellWithTerminal:(nullable NSString*)terminalType
                    withterminalSize:(nullable CGSize (^)(void))requestterminalSize
                       withWriteData:(nullable NSString* (^)(void))requestWriteData
                          withOutput:(void (^)(NSString * _Nonnull))responseDataBlock
             withContinuationHandler:(BOOL (^)(void))continuationBlock;

On execution, once your status is changed, to apply your status quickly, call explicitRequestStatusPickup(). Take an example, when shouldTerminate changes, call this function to terminate this channel immediately or wait for the event loop to pick up on a guaranteed schedule.

- (void)explicitRequestStatusPickup;

Thread Safe

We implemented thread safe by using NSEventLoop to serialize single NSRemoteShell instance. Multiple NSRemoteShell object will be executed in parallel. Channel operations will be executed in serial for each NSRemoteShell.

@interface TSEventLoop : NSObject


- (void)explicitRequestHandle;
- (void)delegatingRemoteWith:(NSRemoteShell*)object;


The event loop will guarantee status pickup is thread safe, called several times per second. To improve the performance and user experience, we use a dispatch source of your session's socket to trigger the event loop handler when you have at least one channel opened when data arrived. Check following code to see how it works.

- (void)unsafeDispatchSourceMakeDecision

All event loop will call a NSRemoteShell objects' handleRequestsIfNeeded method, we deal with control blocks first, and then iterate over all channel to see if data available.

for (dispatch_block_t invocation in self.requestInvokations) {
    if (invocation) { invocation(); }
[self.requestInvokations removeAllObjects];
for (NSRemoteChannel *channelObject in [self.associatedChannel copy]) {
    [channelObject insanityUncheckedEventLoop];

ARC will take place to disconnect if a shell object is no longer holds. You can close the session manually or let ARC handle it.

- (void)dealloc {
    NSLog(@"shell object at %p deallocating", self);
    [self unsafeDisconnect];


NSRemoteShell is licensed under [MIT License - Lakr's Edition].

- Commercial use
- Modification
- Distribution
- Private use

- NO Liability
- NO Warranty

- NO Conditions

Copyright © 2022 Lakr Aream. All Rights Reserved.


Stars: 12
Last commit: 2 weeks ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics