Skip to main content

Dial

The Dial module provides comprehensive watch face management for peripheral devices, including operations to push, delete, switch, and monitor watch faces. It supports built-in watch faces, cloud watch faces, and custom watch faces with full lifecycle management capabilities.

Prerequisites​

  • Device must be connected and authenticated
  • Cloud watch-face packages must be downloaded to a readable local file before installation
  • Device firmware must support watch face operations
  • Sufficient storage space on device for new watch faces

Data Models​

TSDialModel​

General watch face information model used for device watch face operations.

PropertyTypeDescription
dialIdNSString *Unique identifier for the watch face
dialNameNSString *Display name shown to users
dialTypeTSDialTypeType of watch face (BuiltIn, Customer, or Cloud)
isCurrentBOOLYES if currently active on device
locationIndexUInt8Position index where watch face is stored
versionNSString *Version number for update management

TSDialTime​

PropertyTypeDescription
timeImageUIImage *Time style image, preferred over the path
timeImagePathNSString *Local or bundle image path
timePositionTSDialTimePositionFallback position when timeRect is unset
timeRectCGRectTime area in background-image pixel coordinates
timeColorUIColor *Optional tint for monochrome time images
styleTSDialTimeStyleBuilt-in time-style identifier

TSDialDraft and TSDialDraftItem​

Create TSDialDraft with initWithDraftType:templateFilePath:items:. Its draft type, template path, and items are read-only; dialId, previewImage, and multiplePlayIntervalMillis are optional inputs. Create items with itemWithImage:time: or itemWithVideoFilePath:time:; image pixels must match the device screen size. TSDialTime is immutable and uses its designated initializer.

TSDialDanMuItem​

Create a scrolling image with initWithImage:. Only TSDialDraftTypeDanMu uses it, and the background item's danMuItems must contain at least one element. A static image is required; animationFilePath optionally references a readable local GIF. walkSpeed is a finite positive number in pixels/second (default 60); leftToRight defaults to NO.

imageX, imageY, animationX, and animationY use TSDialDanMuCoordinate, containing anchor and offset. Construct a coordinate with TSDialDanMuCoordinateMake(anchor, offset). Offsets are signed pixels in the range -524288…524287. Absolute coordinates use the offset directly; other anchors align the original content size on that axis, then add the offset.

TSDialDanMuAnchorValueMeaning
TSDialDanMuAnchorAbsolute0Absolute top-left coordinate
TSDialDanMuAnchorStart1Left on X, top on Y
TSDialDanMuAnchorCenter2Center on the axis
TSDialDanMuAnchorEnd3Right on X, bottom on Y

Current unified dial models​

ModelKey propertiesPurpose
TSDialCapabilitysupportsCustom, supportsVideo, maxVideoDuration, supportsSlideshow, supportsDanMu, maxSlideshowImages, colorTintable, supportsComponent, shouldRenderTimeInPreview, maxInstallCount, maxInnerCount, screenWidth, screenHeight, deviceCornerRadius, previewWidth, previewHeight, previewCornerRadius, shapeRead-only static capability snapshot
TSStorageSpaceavailable, totalWatch-face storage in bytes; total == 0 means unknown
TSDialDraftdialId, draftType, templateFilePath, items, previewImage, multiplePlayIntervalMillisCustom-dial build input
TSDialDraftItemitemType, image, videoFilePath, time, danMuItemsOne image or video resource
TSDialDanMuItemimage, imageX, imageY, walkSpeed, leftToRight, animationFilePath, animationX, animationYOne scrolling image with an optional associated GIF
TSDialArtifactdialId, dialType, filePathThe only install input, obtained from a custom build or constructed for a downloaded cloud package
TSComposePreviewInputbackgroundImage, time, maxKBSize, keepTransparentBackgroundPreview-composition input
TSCustomDialStyleOptionstyle, previewImageURL, sizeAvailable time style
TSCustomDialPositionOptionposition, frameAvailable time position
TSCustomDialStyleConstraintscreenSize, screenShape, styles, positions, allowColorTintProvider-neutral custom-style constraints

Build and preview rules​

Check dialCapability before building; it may be nil until cached device information is available. maxInstallCount == -1 means unlimited; 0 means custom/cloud installation is unavailable. maxSlideshowImages == 0 means no explicit image limit. Use each support flag before selecting a draft type.

Single-image, video, and DanMu drafts require exactly one item; multiple-image drafts require one or more image items. Time configuration is required except for DanMu. A nil template path lets the Provider resolve a compatible template. Nonpositive multiplePlayIntervalMillis uses the Provider default; positive values are quantized during build. Keep video and GIF files readable and unchanged until build completes.

shouldRenderTimeInPreview controls the Provider's time overlay policy for generated installation previews, independently of supportsComponent. It requires a time configuration and does not control the device's live clock or modify an App-supplied preview. A supplied draft.previewImage must match TSPeripheralScreen.dialPreviewSize in pixels.

fetchDialStorage: returns watch-face storage only. available remains usable when total is unknown. TSStorageSpace also provides used, usagePercentage, hasEnoughSpaceForSize:, and formattedStorageInfo; an unknown total yields 0 for used space and percentage, which must not be interpreted as actual usage.

Enumerations​

TSDialType​

Watch face type enumeration.

ValueNameDescription
0eTSDialTypeBuiltInBuilt-in watch face from device
1eTSDialTypeCustomerCustom watch face created by user
2eTSDialTypeCloudWatch face downloaded from cloud

TSDialDraftType​

Custom watch face resource type enumeration.

ValueNameDescription
1TSDialDraftTypeSingleImageSingle image-based custom watch face
2TSDialDraftTypeMultipleImageMultiple images-based custom watch face
3TSDialDraftTypeVideoVideo-based custom watch face
4TSDialDraftTypeDanMuDanMu image-based watch face

TSDialTimePosition​

Time display position enumeration.

ValueNameDescription
0eTSDialTimePositionTopTop position
1eTSDialTimePositionBottomBottom position
2eTSDialTimePositionLeftLeft position
3eTSDialTimePositionRightRight position
4eTSDialTimePositionTopLeftTop-left corner
5eTSDialTimePositionBottomLeftBottom-left corner
6eTSDialTimePositionTopRightTop-right corner
7eTSDialTimePositionBottomRightBottom-right corner
8eTSDialTimePositionCenterCenter position

TSDialInstallResult​

Watch face install operation result enumeration.

ValueNameDescription
0eTSDialInstallResultStartInstall operation started
1eTSDialInstallResultProgressInstall in progress
2eTSDialInstallResultSuccessInstall completed successfully
3eTSDialInstallResultFailedInstall operation failed
4eTSDialInstallResultCompletedInstall flow ended

Build types and errors​

  • TSDialDraftType: TSDialDraftTypeSingleImage, TSDialDraftTypeMultipleImage, TSDialDraftTypeVideo, TSDialDraftTypeDanMu.
  • TSDialDraftItemType: TSDialDraftItemTypeImage, TSDialDraftItemTypeVideo.
  • TSDialErrorCode: TSDialErrorInvalidDraftType, TSDialErrorMissingTemplateFilePath, TSDialErrorTemplateFileNotFound, TSDialErrorEmptyDraftItems, TSDialErrorInvalidDraftItemCount, TSDialErrorInvalidDraftItemType, TSDialErrorMissingDraftItemTime, TSDialErrorMissingImageResource, TSDialErrorMissingVideoFilePath, TSDialErrorVideoFileNotFound, TSDialErrorInvalidScreenSize, TSDialErrorImageSizeMismatch, TSDialErrorMissingArtifactDialId, TSDialErrorMissingArtifactFilePath, TSDialErrorArtifactFileNotFound, TSDialErrorMissingPreviewBackgroundImage, TSDialErrorInvalidArtifactDialType, TSDialErrorInvalidDanMuItem.

TSDialTimeStyle​

Time display style enumeration.

ValueNameDescription
0eTSDialTimeStyleNoneNo style
1eTSDialTimeStyle1Style 1
2eTSDialTimeStyle2Style 2
3eTSDialTimeStyle3Style 3
4eTSDialTimeStyle4Style 4
5eTSDialTimeStyle5Style 5
6eTSDialTimeStyle6Style 6
7eTSDialTimeStyle7Style 7

Callback types​

CallbackDescription
TSDialInstallCompletionBlockRequired final install result and error; inspect result to determine the outcome
TSDialInstallProgressBlockInstall state and 0–100 progress
TSDialListBlockWatch-face list and error
TSDialWidgetsBlockSupported widget dictionary and error for Fw devices
TSCustomDialStyleConstraintBlockStyle constraints and error

Current API Reference​

Capability, style, and queries​

- (nullable TSDialCapability *)dialCapability;
- (BOOL)isSupportCustomDialStyleConstraint;
- (void)fetchCustomDialStyleConstraint:(TSCustomDialStyleConstraintBlock)completion;
- (void)fetchAllDials:(TSDialListBlock)completion;
- (void)fetchCurrentDial:(void (^)(TSDialModel *_Nullable dial, NSError *_Nullable error))completion;
- (void)fetchDialStorage:(void (^)(TSStorageSpace *_Nullable storage, NSError *_Nullable error))completion;

The capability snapshot comes from cached device information. Style constraints, dial state, and storage are returned asynchronously.

Selection, build, and preview​

- (void)selectDial:(NSString *)dialId completion:(TSCompletionBlock)completion;
- (void)buildDialWithDraft:(TSDialDraft *)draft
completion:(void (^)(TSDialArtifact *_Nullable artifact, NSError *_Nullable error))completion;
- (void)composeDialPreview:(TSComposePreviewInput *)input
completion:(void (^)(UIImage *_Nullable previewImage, NSError *_Nullable error))completion;

The App prepares cropped or trimmed resources at the target screen size, then passes the returned TSDialArtifact to the install API.

Install, uninstall, and events​

- (void)installDial:(TSDialArtifact *)artifact
progressBlock:(nullable TSDialInstallProgressBlock)progressBlock
completion:(TSDialInstallCompletionBlock)completion;
- (void)cancelDialInstall:(TSCompletionBlock)completion;
- (void)uninstallDial:(NSString *)dialId completion:(TSCompletionBlock)completion;
- (void)registerDialListDidChangeHandler:(void (^)(NSArray<TSDialModel *> *_Nullable allDials,
NSError *_Nullable error))handler;
- (void)fetchSupportedDialWidgets:(TSDialWidgetsBlock)completion;

Install progress uses 0–100. Built-in dials cannot be uninstalled. Only the latest list-change handler is retained.

Current API examples​

Use these snippets independently inside your App's methods after SDK initialization and connection. Replace the sample asset names, paths, and IDs with your own resources. Obtain the interface first:

id<TSPeripheralDialInterface> dialInterface = [TopStepComKit sharedInstance].dial;
if (dialInterface == nil) {
TSLog(@"[Dial] Interface unavailable");
return;
}

Query capabilities and installed watch faces​

TSDialCapability *capability = [dialInterface dialCapability];
if (capability == nil) {
TSLog(@"[Dial] Device capabilities are not available yet");
return;
}
TSLog(@"custom=%@ video=%@ maxVideo=%ld sec",
capability.supportsCustom ? @"YES" : @"NO",
capability.supportsVideo ? @"YES" : @"NO",
(long)capability.maxVideoDuration);
[dialInterface fetchAllDials:^(NSArray<TSDialModel *> *dials, NSError *error) {
if (error) {
TSLog(@"[Dial] List failed: %@", error.localizedDescription);
return;
}
for (TSDialModel *dial in dials) {
TSLog(@"%@ %@", dial.dialName, dial.isCurrent ? @"(current)" : @"");
}
}];
[dialInterface fetchCurrentDial:^(TSDialModel *dial, NSError *error) {
if (!error && dial) {
TSLog(@"[Dial] Current watch face: %@", dial.dialName);
}
}];

Query style constraints and storage​

if ([dialInterface isSupportCustomDialStyleConstraint]) {
[dialInterface fetchCustomDialStyleConstraint:^(TSCustomDialStyleConstraint *constraint, NSError *error) {
if (!error && constraint) {
TSLog(@"[Dial] Available styles: %lu", (unsigned long)constraint.styles.count);
}
}];
}
[dialInterface fetchDialStorage:^(TSStorageSpace *storage, NSError *error) {
if (error || !storage) {
TSLog(@"[Dial] Storage query failed: %@", error.localizedDescription);
return;
}
TSLog(@"[Dial] Available: %llu bytes", storage.available);
if (storage.total > 0) {
TSLog(@"[Dial] Total: %llu bytes", storage.total);
}
}];

Build, preview, and install a single-image watch face​

UIImage *image = [UIImage imageNamed:@"dial_background"];
if (!image || ![dialInterface dialCapability].supportsCustom) {
TSLog(@"[Dial] Provide a screen-sized image on a device that supports custom watch faces");
return;
}
TSDialTime *time = [[TSDialTime alloc] initWithTimeImage:[UIImage imageNamed:@"time_style"]
timeImagePath:nil
timePosition:eTSDialTimePositionTop
timeRect:CGRectZero
timeColor:nil
style:eTSDialTimeStyle1];
TSDialDraftItem *item = [TSDialDraftItem itemWithImage:image time:time];
TSDialDraft *draft = [[TSDialDraft alloc] initWithDraftType:TSDialDraftTypeSingleImage
templateFilePath:nil items:@[item]];
[dialInterface composeDialPreview:[[TSComposePreviewInput alloc]
initWithBackgroundImage:image time:time maxKBSize:300 keepTransparentBackground:NO]
completion:^(UIImage *preview, NSError *error) {
if (!error && preview) {
TSLog(@"[Dial] Preview generated: %@", preview);
}
}];
[dialInterface buildDialWithDraft:draft completion:^(TSDialArtifact *artifact, NSError *error) {
if (error || !artifact) {
TSLog(@"[Dial] Build failed: %@", error.localizedDescription);
return;
}
[dialInterface installDial:artifact progressBlock:^(TSDialInstallResult result, NSInteger progress) {
TSLog(@"Install progress: %ld%%", (long)progress);
} completion:^(TSDialInstallResult result, NSError *error) {
TSLog(@"[Dial] Install result: %ld, error: %@", (long)result, error.localizedDescription);
}];
}];

Video draft​

Reuse the time configuration from the single-image example. Prepare the video at the device screen size, within maxVideoDuration, and keep the file readable until the build finishes.

if (![dialInterface dialCapability].supportsVideo) {
TSLog(@"[Dial] Video watch faces are not supported");
return;
}
TSDialDraftItem *item = [TSDialDraftItem itemWithVideoFilePath:@"/path/to/watchface.mp4" time:time];
TSDialDraft *draft = [[TSDialDraft alloc] initWithDraftType:TSDialDraftTypeVideo
templateFilePath:nil items:@[item]];
[dialInterface buildDialWithDraft:draft completion:^(TSDialArtifact *artifact, NSError *error) {
if (error || !artifact) {
TSLog(@"[Dial] Build failed: %@", error.localizedDescription);
return;
}
[dialInterface installDial:artifact progressBlock:nil completion:^(TSDialInstallResult result, NSError *installError) {
TSLog(@"[Dial] Video install result: %ld, error: %@", (long)result, installError.localizedDescription);
}];
}];

Select, cancel, or uninstall​

These are independent actions; do not run them together after starting an installation. Select an ID from fetchAllDials: and uninstall only custom or cloud watch faces.

[dialInterface selectDial:@"dial-id" completion:^(BOOL success, NSError *error) {
TSLog(@"Selection: %@", success ? @"success" : error.localizedDescription);
}];
[dialInterface cancelDialInstall:^(BOOL success, NSError *error) {
TSLog(@"Cancel: %@", success ? @"success" : error.localizedDescription);
}];
[dialInterface uninstallDial:@"custom-dial-id" completion:^(BOOL success, NSError *error) {
TSLog(@"Uninstall: %@", success ? @"success" : error.localizedDescription);
}];

Observe list changes and supported widgets​

[dialInterface registerDialListDidChangeHandler:^(NSArray<TSDialModel *> *dials, NSError *error) {
if (!error && dials) {
TSLog(@"[Dial] Watch-face list changed: %lu", (unsigned long)dials.count);
}
}];
[dialInterface fetchSupportedDialWidgets:^(NSDictionary *widgets, NSError *error) {
if (!error && widgets) {
TSLog(@"[Dial] Supported widgets: %@", widgets);
}
}];

Important notes​

  1. Use TSDialDraft, TSDialDraftItem, and TSDialTime for the current custom watch-face build flow; image pixels must match the device screen size.
  2. TSStorageSpace values are bytes. Check available before installing; total == 0 means unknown.
  3. Single-image, video, and DanMu drafts require one item; multiple-image drafts accept multiple image items, and DanMu requires non-empty danMuItems.
  4. Installation progress is 0–100. Built-in watch faces cannot be uninstalled.
  5. A new registerDialListDidChangeHandler: registration replaces the previous handler. The event does not identify whether an install, uninstall, or selection caused the change.