ArtAdk

1.0.2

aiotrixdev/art-objectivec-adk

What's New

1.0.2

2026-07-28T07:12:01Z

Full Changelog: 1.0.1...1.0.2

ART Objective-C ADK

Objective-C Platform SwiftPM License

Objective-C SDK for ART – A Realtime Tech Communication, a realtime communication platform for building intelligent applications with WebSocket-based messaging, AI Agents, AI Orchestrators, presence tracking, end-to-end encrypted channels, and CRDT-backed shared objects.


Features

  • WebSocket connection management (connect, pause, resume, auto-reconnect)
  • Channel subscriptions for default, targeted, secure, and shared-object/CRDT channels
  • Structured message pushing with payloads and optional recipients
  • Event-based message listening via emitter, listen, and bind
  • Real-time presence tracking
  • End-to-end encryption support
  • Interceptors for message processing
  • Shared objects using CRDT
  • AI Agent integration
  • AI Orchestrator workflows

Installation

Swift Package Manager

dependencies: [
    .package(
        url: "https://github.com/aiotrixdev/art-objectivec-adk.git",
        from: "1.0.2"
    )
]

CocoaPods

Add the dependency to your Podfile:

pod 'ArtAdk', '~> 1.0.2'

Then run:

pod instal

You can also add the package directly in Xcode via:

File → Add Packages → Paste repository URL

Import the SDK where you need it:

#import <ArtAdk/ADK.h>

Configuration

1. Create credentials

Store your ART credentials securely:

CredentialStore *creds = [[CredentialStore alloc]
    initWithEnvironment:@"YOUR_ENV"
             projectKey:@"YOUR_PROJECT_KEY"
               orgTitle:@"YOUR_ORG"
               clientID:@"CLIENT_ID"
           clientSecret:@"CLIENT_SECRET"
                 config:nil
            accessToken:nil];

2. Get a user passcode

ART uses a short-lived passcode for authentication:

- (void)fetchPasscodeWithCredentials:(CredentialStore *)creds
                          completion:(void (^)(NSString *passcode, NSError *error))completion {
    NSURL *url = [NSURL URLWithString:@"https://dev.arealtimetech.com/ws/v1/connect/passcode"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";

    [request setValue:creds.clientID forHTTPHeaderField:@"Client-Id"];
    [request setValue:creds.clientSecret forHTTPHeaderField:@"Client-Secret"];
    [request setValue:creds.orgTitle forHTTPHeaderField:@"X-Org"];
    [request setValue:creds.environment forHTTPHeaderField:@"Environment"];
    [request setValue:creds.projectKey forHTTPHeaderField:@"ProjectKey"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

    NSDictionary *body = @{
        @"username": @"john_doe",
        @"first_name": @"John",
        @"last_name": @"Doe"
    };

    NSError *jsonError = nil;
    request.HTTPBody = [NSJSONSerialization dataWithJSONObject:body
                                                       options:0
                                                         error:&jsonError];

    if (jsonError) {
        completion(nil, jsonError);
        return;
    }

    [[[NSURLSession sharedSession] dataTaskWithRequest:request
                                     completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            completion(nil, error);
            return;
        }

        NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSDictionary *dataObj = [json isKindOfClass:[NSDictionary class]] ? json[@"data"] : nil;
        NSString *passcode = [dataObj isKindOfClass:[NSDictionary class]] ? dataObj[@"passcode"] : nil;

        completion(passcode ?: @"", nil);
    }] resume];
}

Quick Start

#import <ArtAdk/ADK.h>

CredentialStore *creds = [[CredentialStore alloc]
    initWithEnvironment:@"YOUR_ENV"
             projectKey:@"YOUR_PROJECT_KEY"
               orgTitle:@"YOUR_ORG"
               clientID:@"CLIENT_ID"
           clientSecret:@"CLIENT_SECRET"
                 config:nil
            accessToken:nil];

[self fetchPasscodeWithCredentials:creds
                        completion:^(NSString *passcode, NSError *error) {
    if (error || passcode.length == 0) {
        NSLog(@"Passcode error: %@", error);
        return;
    }

    creds.accessToken = passcode;

    AdkConfig *config = [[AdkConfig alloc]
        initWithUri:@"YOUR_WEBSOCKET_URI"
          authToken:passcode
     getCredentials:^CredentialStore *{
        return creds;
     }
               root:nil];

    Adk *adk = [[Adk alloc] initWithConfig:config];

    [adk on:@"connection" handler:^(id data) {
        if ([data isKindOfClass:[ConnectionDetail class]]) {
            ConnectionDetail *conn = (ConnectionDetail *)data;
            NSLog(@"Connected → %@", conn.connectionId);
        }
    }];

    [adk on:@"close" handler:^(id reason) {
        NSLog(@"Closed: %@", reason);
    }];

    [adk connect:nil completion:^{
        [adk subscribe:@"room-42"
            completion:^(BaseSubscription *subscription, NSError *subError) {
                if (subError || !subscription) {
                    NSLog(@"Subscribe error: %@", subError);
                    return;
                }

                [subscription.emitter on:@"message" handler:^(id data) {
                    NSLog(@"Received: %@", data);
                }];

                [subscription push:@"message"
                              data:@{@"text": @"Hello ART!"}
                           options:nil
                        completion:^(NSError *pushError) {
                            if (pushError) {
                                NSLog(@"Push error: %@", pushError);
                            }
                        }];
            }];
    }];
}];

Connecting

[adk connect:nil completion:^{
    NSLog(@"Connected");
}];

Safe usage:

do {
    [adk connect:nil completion:^{
        NSLog(@"Connected");
    }];
} while (0);

Check the current state:

[adk getState]; // connected | retrying | paused | stopped

Subscribing to a Channel

[adk subscribe:@"room-42"
    completion:^(BaseSubscription *subscription, NSError *error) {
        if (error || !subscription) {
            NSLog(@"Subscribe error: %@", error);
            return;
        }

        if ([subscription isKindOfClass:[LiveObjSubscription class]]) {
            LiveObjSubscription *live = (LiveObjSubscription *)subscription;
            NSLog(@"Shared-object channel: %@", live);
        }
    }];

Unsubscribe:

[subscription unsubscribe:^{
    NSLog(@"Unsubscribed");
}];

Pushing Messages

[subscription push:@"message"
              data:@{@"text": @"Hello"}
           options:nil
        completion:^(NSError *error) {
            if (error) {
                NSLog(@"Push error: %@", error);
            }
        }];

Targeted messaging:

PushConfig *options = [[PushConfig alloc] initWithTo:@[@"bob"]];

[subscription push:@"message"
              data:@{@"text": @"Hi Bob"}
           options:options
        completion:^(NSError *error) {
            if (error) {
                NSLog(@"Push error: %@", error);
            }
        }];

For targeted and secure channels, exactly one recipient should be provided.


Receiving Messages

[subscription.emitter on:@"message" handler:^(id data) {
    NSLog(@"Got: %@", data);
}];

Or receive every event on the channel:

if ([subscription isKindOfClass:[Subscription class]]) {
    Subscription *typedSub = (Subscription *)subscription;
    [typedSub listen:^(NSDictionary<NSString *, id> *message) {
        NSLog(@"Event: %@, content: %@", message[@"event"], message[@"content"]);
    }];
}

Presence

__block PresenceUnsubscribe stopPresence = nil;

[subscription fetchPresence:YES
                   callback:^(NSArray<NSString *> *users) {
                       NSLog(@"Online: %@", users);
                   }
                 completion:^(PresenceUnsubscribe unsubscribe, NSError *error) {
                     if (error) {
                         NSLog(@"Presence error: %@", error);
                         return;
                     }

                     stopPresence = unsubscribe;
                 }];

// later
if (stopPresence) {
    stopPresence(^{
        NSLog(@"Presence listener removed");
    });
}

Pass YES to receive unique usernames, or NO to receive the raw presence list.


Encrypted Channels

[adk generateKeyPair:^(KeyPairType *keyPair, NSError *error) {
    if (error || !keyPair) {
        NSLog(@"Key pair error: %@", error);
        return;
    }

    [adk subscribe:@"SECURE_CHANNEL"
        completion:^(BaseSubscription *subscription, NSError *subError) {
            if (subError || !subscription) {
                NSLog(@"Secure subscribe error: %@", subError);
                return;
            }

            PushConfig *options = [[PushConfig alloc] initWithTo:@[@"bob"]];

            [subscription push:@"message"
                          data:@{@"text": @"Private"}
                       options:options
                    completion:^(NSError *pushError) {
                        if (pushError) {
                            NSLog(@"Secure push error: %@", pushError);
                        }
                    }];

            [subscription.emitter on:@"message" handler:^(id data) {
                NSLog(@"Decrypted: %@", data);
            }];
        }];
}];

Shared Object Channels (CRDT)

[adk subscribe:@"CRDT_CHANNEL"
    completion:^(BaseSubscription *subscription, NSError *error) {
        if (error || ![subscription isKindOfClass:[LiveObjSubscription class]]) {
            NSLog(@"CRDT subscribe error: %@", error);
            return;
        }

        LiveObjSubscription *live = (LiveObjSubscription *)subscription;

        // Write
        CRDTProxy *document = [[live state] objectForKeyedSubscript:@"document"];
        [[document objectForKeyedSubscript:@"title"] set:@"My Doc"];
        [live flush:^{}];

        // Read
        [[live query:@"document"] executeWithCompletion:^(id result) {
            NSLog(@"Snapshot: %@", result);
        }];

        // Listen
        void (^dispose)(void) = [[live query:@"document"] listenWithCallback:^(id value) {
            NSLog(@"Updated: %@", value);
        }];

        dispose();
    }];

Array Operations

CRDTProxy *items = [[live state] objectForKeyedSubscript:@"items"];

[items pushItem:@"alpha"];
[items unshiftItem:@"zero"];
[items pop];
[items removeAtIndex:2];
[items spliceStart:1 deleteCount:1 insertItems:@[@"x" ]];

[items flushWithCompletion:^{}];

Interceptors

[adk intercept:@"filter"
             fn:^(NSDictionary *request, InterceptorResolve resolve, InterceptorReject reject) {
                 NSString *text = [request[@"text"] isKindOfClass:[NSString class]] ? request[@"text"] : @"";

                 if ([text containsString:@"anyword"]) {
                     reject(@"Blocked");
                     return;
                 }

                 resolve(request);
             }
     completion:^(Interception *interception, NSError *error) {
         if (error) {
             NSLog(@"Interceptor error: %@", error);
         }
     }];

Connection Lifecycle

[adk on:@"connection" handler:^(id data) {
    NSLog(@"Connected");
}];

[adk on:@"close" handler:^(id reason) {
    NSLog(@"Closed");
}];

[adk pause];
[adk resume:^{
    NSLog(@"Resumed");
}];
[adk disconnect:^{
    NSLog(@"Disconnected");
}];

Agent Lab

ART ADK provides two AI integrations:

  • Agent — interact with a single AI agent.
  • Orchestrator — execute multi-agent workflows coordinated by an orchestrator.

Agent

Connect to an Agent Builder agent and start a conversation.

Agent *agent = [adk agent:@"YOUR_AGENT_ID"];
AgentThread *thread = [agent thread];

[thread listen:^(AgentEventEnvelope *envelope) {
    NSLog(@"Envelope: %@", envelope);
}];

[thread run:@"Plan a 3-day trip to Dubai"
     replyId:nil
  completion:^(Run *run, NSError *error) {
      if (error || !run) {
          NSLog(@"Run error: %@", error);
          return;
      }

      [run done:^(AgentOutput *output, AgentError *agentError) {
          if (agentError) {
              NSLog(@"Agent error: %@", agentError);
              return;
          }

          NSLog(@"Output: %@", output);
      }];
  }];

Human-in-the-Loop (HITL)

When an agent requires additional information, register a feedback handler before starting the run.

[thread feedbackRequest:^(HumanInputRequest *request, Run *run) {
    [run sendFeedback:@"Budget 50,000 travelling in December"
           completion:^(NSError *error) {
               if (error) {
                   NSLog(@"Feedback error: %@", error);
               }
           }];
}];

Orchestrator

Connect to an Orchestrator Builder workflow.

Orchestrator *orchestrator = [adk orchestrator:@"YOUR_ORCHESTRATOR_ID"];

[orchestrator thread:^(OrchestratorThread *thread, NSError *error) {
    if (error || !thread) {
        NSLog(@"Thread error: %@", error);
        return;
    }

    [thread listen:^(NSDictionary<NSString *, id> *message) {
        NSLog(@"Event: %@", message);
    }];

    [thread push:@"user_input"
            data:@{@"message": @"Plan a 3-day trip to Goa"}
      completion:^(NSError *pushError) {
          if (pushError) {
              NSLog(@"Push error: %@", pushError);
          }
      }];
}];

Documentation

Full documentation is available at: https://docs.arealtimetech.com/docs/adk/

Topic Link
Overview ADK Overview
Installation Objective-C Installation
Publish & Subscribe Pub/Sub Docs
Connection Management Connection Docs
User Presence Presence Docs
Encrypted Channels Encryption Docs
Shared Object Channels Shared Object Docs
Interceptors Interceptor Docs
Agents Agent Docs
Orchestrator Orchestrator Docs

License

Released under the MIT License.

Description

  • Swift Tools 5.9.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Aug 23 2026 14:39:51 GMT-0900 (Hawaii-Aleutian Daylight Time)