日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

小組件開發最佳實踐(iOS)

應用程序小組件是一個微型的應用程序視圖,可以嵌入其他應用程序(例如主屏幕)中并接收定期更新。本文檔介紹了開發iOS小組件。

創建證書

iOS中小組件(Widget)是一個獨立的應用,可以看做是一個獨立的App(宿主App的拓展程序),所以我們需要對Widget單獨創建證書。

  1. 創建宿主App證書。

    該部分操作資料很豐富,此處不做介紹,可自行查找相關資料。

  2. 創建Widget證書。

    創建Widget證書的操作與創建宿主App證書的操作類似,但需注意以下幾點。

    • Widget的Bundle Id是以宿主App為基礎擴展的,例如宿主App為com.companyName.AppName,則Widget的格式應該為com.companyName.AppName.WidgetName配置證書示例

    • 創建證書的時候,需勾選App Group配置項。勾選App Groups

創建Widget

  1. 在xcode中,選擇File > New > Target > Today Extension,創建Today。

    創建today

  2. 查看創建后的目錄結構。

    目錄結構

  3. 設置Widget工程的開發方式。

    工程默認為storyboard開發方式,如果想使用純代碼方式,則需要進行以下操作。

    TodayWidget > Info.plist > Extension中,刪除NSExtensionMainStoryboard選項 ,增加NSExtensionPrincipalClass選項,value為類的名字IMSWidgetTestViewController,如下圖所示。

    配置示例

  4. 設置Widget的展開與折疊效果,示例如下。

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        if (/*折疊展開判斷 */) {
            self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
        } else {
            self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;
        }
    }
    
    - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
        switch (activeDisplayMode) {
            case NCWidgetDisplayModeCompact: {
                self.preferredContentSize = maxSize;
                break;
            }
            case NCWidgetDisplayModeExpanded: {
                self.preferredContentSize = CGSizeMake(self.view.bounds.size.width, 210);
                break;
            }
            default:
                break;
        }
    }
    說明
    • 展開的高度可以自行設置,不超過系統最大值即可。

    • 系統不支持折疊高度的修改。

  5. 刷新數據,建議使用系統提供的方法,示例如下。

    - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
        completionHandler(NCUpdateResultNewData);
    }
    說明

    此處刷新有可能執行失敗,這是目前Apple存量的問題,可通過延遲來解決。詳細請參見延時的原因臨時解決方案

  6. 配置小組件與宿主App的跳轉功能。

    Extension和宿主App是兩個完全獨立的進程,它們之間不能直接通信(即無法通過單擊應用內部按鈕跳轉到指定頁面)。為了實現Widget調起宿主App,這里通過openURL的方式來啟動宿主App。

    1. 在宿主App里選擇Targets > MCWidgetDemo > Info > Url Types,添加URL Schemes。

      下圖為設置示例,設置URL SchemesTodayWidget

      配置URL Schemes

    2. 配置代碼跳轉地址(openURL)。完整地址為:”URL Schemes” + “://” + “宿主App Bundle Id”,如下圖所示。

      配置跳轉地址

設置Widget和宿主App交互通信

因為Widget的獨立性,宿主App要與Widget之間相互通信,需要通過App Group來實現。

  1. 創建App Group。

    前往開發者網站注冊一個App Group,填入名字和id,并根據界面提示操作,即可得到下圖類似的App Group。App Group

  2. Target > Signing & Capabilities > App Group下,配置App Group。

    在宿主App和擴展程序(Widget)的App Group中,分別設置group名稱。需確保宿主App和Widget的groupName相同,并且與在開發者網站注冊的App Groups保持一致。配置示例

  3. 配置Widget和宿主App之間交互通信。

    使用NSUserDefaults或者NSFileManager方式都可以實現Widget和宿主App之間交互通信。此處介紹如何使用NSUserDefaults方式實現交互通信。

    • 存數據存數據

    • 取數據取數據

生活物聯網平臺SDK使用指導

下面主要介紹TodayExtension的開發過程,其余Widget開發請參照Apple官方文檔自行完成。

  1. 引入SDK。

    1. 設置Profile。

      iOS推薦使用Cocoapods引入,分別對宿主App Target和Widget Target引入SDK。因為Widget是獨立的應用,所以兩個Target都需要各自引入編譯所需的SDK,多個小組件,就配置多份Profile。配置示例如下。

      target “WidgetTargetName1” do
          pod 'IMSApiClient', '1.6.0'
          pod 'IMSAuthentication', '1.4.1'
      end
      
      target “WidgetTargetName2” do
          pod 'IMSApiClient', '1.6.0'
          pod 'IMSAuthentication', '1.4.1'
      end
    2. 查看小組件開發必備SDK列表。

      小組件開發必備SDK列表
      【1】通用請求SDK
          pod 'IMSApiClient', '1.6.0'
          pod 'IMSAuthentication', '1.4.1'
      【2】設備小組件相關SDK
          # 物
          pod 'IMSThingCapability', '1.7.5'
          # 長連接
          pod 'IMSMobileChannel', '1.6.7'
    3. 執行pod update,并編譯工程。

      編譯成功后,選擇Widget的target,運行小組件工程。

      說明

      因為Widget的獨立,安全圖片也需要導入一份到Widget的Target下,否則會報錯。

  2. 初始化宿主App配置。

    1. 初始化宿主App的IMSAuthentication,示例代碼如下。

      // 設置需要更新的Credential至AppGroup中
      [[IMSCredentialManager sharedManager] addCredentialStoreWithAppGroupName:AppGroupName];
    2. 把ApiClient的信息,寫入對應的AppGroup共享區域中。

      // 宿主App初始化IMSApiClient完成后,把ApiClient的信息,寫入到對應的AppGroup共享區域中
      [[IMSConfiguration sharedInstance] storeConfigToAppGroup:AppGroupName];
  3. 配置TodayExtension,配置示例代碼如下。

    + (void)initialize {
        // 初始化APIClient
            [IMSConfiguration initWithAppGroupName:AppGroupName];
            // 初始化身份認證
            [IMSCredentialManager initWithAppGroupName:AppGroupName];
        // 注冊RequestClient的代理
        IMSIoTAuthentication *iotAuthDelegate = [[IMSIoTAuthentication alloc] initWithCredentialManager:IMSCredentialManager.sharedManager];
        [IMSRequestClient registerDelegate:iotAuthDelegate forAuthenticationType:IMSAuthenticationTypeIoT];
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        // 根據AppGroup共享區域存儲的信息
        // 防止出現未打開宿主App、已經初始化Extension的APIClient
        [[IMSConfiguration sharedInstance] synchronizeConfigFromAppGroup];
        // 從UserDefaults更新Credential
        [[IMSCredentialManager sharedManager] synchronizeCredentialFromAppGroup];
    }
  4. 調用接口,示例代碼如下。

     IMSIoTRequestBuilder *builder = [[IMSIoTRequestBuilder alloc] initWithPath:@"/uc/path/xxxx"
                                                                        apiVersion:@"1.0.0"
                                                                            params:@{}];
        [builder setScheme:@"https"];
        IMSRequest *request = [[builder setAuthenticationType:IMSAuthenticationTypeIoT] build];
        __weak typeof(self) weakSelf = self;
        [IMSRequestClient asyncSendRequest:request responseHandler:^(NSError * _Nullable error, IMSResponse * _Nullable response) {
              if (response.code == 401) {
                    [self loginOut];
                }
    
                if (error) {
                    NSLog(@"request error = %@",error);
                } else {
                    NSLog(@"request success");
                }
    
            }];
        }];
  5. 實時判斷宿主App登錄狀態,示例代碼如下。

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        // 根據AppGroup共享區域存儲的信息、配置Host、環境、語言、安全圖片
        // 防止出現未打開宿主App、已經初始化Extension的APIClient
            [[IMSConfiguration sharedInstance] synchronizeConfigFromAppGroup];
    
            // 從UserDefaults更新Credential
            [[IMSCredentialManager sharedManager] synchronizeCredentialFromAppGroup];
    
        // 通過Credential是否存在來判斷登錄態
        if ([IMSCredentialManager sharedManager].credential) {
            // 已登錄
        } else {
            // 未登錄
        }
    }
  6. 配置小組件顯示名稱的多語言。

    1. 使用宿主App的 [IMSConfiguration sharedInstance].language更新語言信息,信息需要重新保存到group中。

      // 把ApiClient的信息,寫入到對應的AppGroup共享區域中
      [[IMSConfiguration sharedInstance] storeConfigToAppGroup:AppGroupName];
    2. 設置多語言。

      選中TodayExtension的Target,選擇 New > File > String File,新建 strings 文件(名稱請使用InfoPlist)。

      創建完成后,選中InfoPlist.strings文件,單擊Localize,添加多語言。

      設置多語言

      多語言設置后的界面如下。

      多語言

    3. 更改小組件的顯示名稱。

      選中某種語言,修改該語言下小組件的顯示名稱(小組件名字是系統語言控制的,這個不隨App更改)。

      更改顯示名稱

設備小組件&場景小組件接口文檔和調用過程

設備小組件和場景小組件在開發過程中使用的接口文檔(參見場景服務)和調用示例如下。

  • 宿主App相關的接口

    • 場景小組件

      【1】獲取已經被添加到小組件的場景list
          path:/living/appwidget/list
          version:1.0.0
          params:@{}
      【2】全量場景查詢
          path:/living/scene/query
          version:1.0.1
          params = @{@"catalogId": @"0",
                                   @"pageNo": @(pageNo),
                                   @"pageSize": @(pageSize)
                                   }
      【3】更新場景小組件
        path:/living/appwidget/create
        version:1.0.0
        params = @{@"sceneIds": @[]}
    • 設備小組件

      【1】獲取已經被加到小組件的設備list
          path:/iotx/ilop/queryComponentProduct
          version:1.0.0
          params:@{}
      
      【2】獲取設備的屬性列表(目前屬性多語言需要入參時傳遞)
          path:/iotx/ilop/queryComponentProperty
          version:1.0.0
          params = @{@"productKey":productKey,
                                   @"iotId":iotId,
                                   @"query":@{@"dataType":@"BOOL”, @"I18Language":@"zh-CN"}
                                   }
      【3】小組件列表更新
          path:/iotx/ilop/updateComponentProduct
          version:1.0.0
          params:更改后的設備list
  • TodayExtension相關接口

    • 場景小組件

      【1】獲取已經被添加到小組件的場景list
          path:/living/appwidget/list
          version:1.0.0
          params:@{}
      【2】執行場景
          path:/scene/fire
          version:1.0.1
          params:@{@"sceneId":sceneId}
    • 設備小組件

      【1】獲取已經被加到小組件的設備list
          path:/iotx/ilop/queryComponentProduct
          version:1.0.0
          params:@{}    
      【2】設備小組件,有本地通信和云端通信邏輯,需要集成宿主APP中的長連接綁定 & 訂閱,監聽長連接正常連接
      【3】設備狀態變更,需要自行定位/thing/properties 和  /thing/status 的topic,監聽狀態變更,刷新UI
      【4】選中設備,指定ThingShell設置設備屬性,通過物的模型,變更屬性
      【5】如果訂閱過Topic,設置【4】成功后,也會收到云端的狀態變更通知
    • 設備小組件核心參考代碼

      【1】長連接綁定 & 訂閱(相關SDK參見長連接通道SDK)
        IMSConfiguration * imsconfig = [IMSConfiguration sharedInstance];
        LKAEConnectConfig * config = [LKAEConnectConfig new];
        config.appKey = imsconfig.appKey;
        config.authCode = imsconfig.authCode;
        // 指定長連接服務器地址。 (默認不填,SDK會使用默認的地址及端口。默認為國內華東節點。不要帶 "協議://",如果置為空,底層通道會使用默認的地址)
        config.server = @""
      // 開啟動態選擇Host功能。 (默認 NO,海外環境請設置為 YES。此功能前提為 config.server 不特殊指定。)
        config.autoSelectChannelHost = NO;
        [[LKAppExpress sharedInstance]startConnect:config connectListener:self];// self 需要實現 LKAppExpConnectListener 接口
      }
      【2】注冊下行Listener
      
      #pragma mark - 注冊下行Listener
      static NSString *const IMSiLopExtensionDidReceiveUpdateAttributeSuccess = @"LAMPPANEL_DIDRECEIVE_UPDATE_ATTRIBUTE_SUCCESS";
      static NSString *const IMSiLopExtensionDidReceiveUpdateDeviceStateSuccess = @"LAMPPANEL_DIDRECEIVE_UPDATE_DEVICE_STATE_SUCCESS";
      @class TodayViewController;
      @interface IMSWidgetDeviceListener : NSObject <LKAppExpDownListener>
      @end
      @implementation IMSWidgetDeviceListener
      
      - (void)onDownstream:(NSString * _Nonnull)topic data:(id  _Nullable)data {
          IMSAppExtensionLogVerbose(@"小組件 onDownstream topic : %@", topic);
          IMSAppExtensionLogVerbose(@"小組件 onDownstream data : %@", data);
          NSDictionary * replyDict = nil;
          if ([data isKindOfClass:[NSString class]]) {
              NSData * replyData = [data dataUsingEncoding:NSUTF8StringEncoding];
              replyDict = [NSJSONSerialization JSONObjectWithData:replyData options:NSJSONReadingMutableLeaves error:nil];
          } else if ([data isKindOfClass:[NSDictionary class]]) {
              replyDict = data;
              //這里添加云端處理!
              if (data) {
                  if ([topic isEqualToString:@"/thing/properties"]) {
                      [[NSNotificationCenter defaultCenter] postNotificationName:IMSiLopExtensionDidReceiveUpdateAttributeSuccess object:self userInfo:data];
                  }
      
                  if ([topic isEqualToString:@"/thing/status"]) {
                      [[NSNotificationCenter defaultCenter] postNotificationName:IMSiLopExtensionDidReceiveUpdateDeviceStateSuccess object:self userInfo:data];
                  }
      
              }
          }
          if (replyDict == nil) {
              return;
          }
      }
      
      - (BOOL)shouldHandle:(NSString * _Nonnull)topic {
          // 需要設什么topic,返回什么topic
          if ([topic isEqualToString:@"/thing/properties"] || [topic isEqualToString:@"/thing/status"]) {
              return YES;
          }
          return NO;
      }
      @end
      
      【3】增加代理監聽、增加屬性
      @interface IMSWidgetDeviceController () < LKAppExpConnectListener>
      
      // 本地控制
      @property (nonatomic, strong) IMSWidgetDeviceListener *imsWidgetDeviceListener;
      
      
      【4】在ViewDidLoad 中增加監聽
      - (void)viewDidLoad {
          [super viewDidLoad];
          // Do any additional setup after loading the view from its nib.
      
          // 監聽云端設備屬性變更
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dididReceiveUpdateAttributeNoti:) name:IMSiLopExtensionDidReceiveUpdateAttributeSuccess object:nil];
          // 監聽云端設備狀態變更
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dididReceiveUpdateDeviceStateNoti:) name:IMSiLopExtensionDidReceiveUpdateDeviceStateSuccess object:nil];
      }
      
      【5】自行處理下行通知
      // 云端屬性數據下發
      - (void)dididReceiveUpdateAttributeNoti:(NSNotification *)info {
      }
      
      // 云端狀態數據下發
      - (void)dididReceiveUpdateDeviceStateNoti:(NSNotification *)info {
      }
      
      
      【6】更改屬性方法
      IMSThing *thingShell = [kIMSThingManager buildThing:iotId];
      [[thingShell getThingActions] setProperties:@{propertyIdentifierName:value}
                                       responseHandler:^(IMSThingActionsResponse * _Nullable response) {
                                         if (response.success) {
                                               // 成功
                                           } else {
                                              // 失敗
                                           }
      }];
      
      【7】釋放資源
      - (void)viewWillDisappear:(BOOL)animated {
          [super viewWillDisappear:animated];
          // 移除長鏈接相關
          [[LKAppExpress sharedInstance] removeConnectListener:self];
          [[LKAppExpress sharedInstance] removeDownStreamListener:self.imsWidgetDeviceListener];
      }
      
      - (void)dealloc {
          [kIMSThingManager destroyThing:self.thingShell];
          [[NSNotificationCenter defaultCenter] removeObserver:self];
      }