Icon

Littleor

11166 分类: iOS

[iOS14]WidgetKit开发实战2-开发一言小部件

前言

2020年06月22日的WWDC上iOS14的新特性-小部件正式在iOS上线,同时WidgetKit也正式面向广大开发者使用。

也正是因为对Android的小部件有所了解,故想尝试下iOS的小部件的开发,并且发现当前并没有相关的文章,故记录下我学习WigetKit的经历,以下均为自己学习路上的经历,可能会有些问题,还望大佬指正。

同时已把学习路上写的代码开源 - iWiget,看完这篇文章认为有用就点个Star呗!

项目地址: https://github.com/Littleor/iWidget

没有看过前一节的建议看看这个: (iOS14)WidgetKit开发实战1-初识iOS小部件

效果演示

演示GIF 白天模式 黑夜模式

开发一言小部件

使用WigetKit开发Widget的主要就是View、Provider、Data。简单来说,获取到Data之后使用Provider显示在View上就是Widget了。

1.编写View

一言小部件的View可能是这三个当中最简单的部分了,这里为了方便理解直接使用一个Text来表示吧。

struct OneWordView: View {
    var content:String = "每日一言"
    var body: some View {
        Text(content)
    }
}

如上述代码,content用来控制显示内容,一个最简单的Text显示即可,还能靠Swift实现自动暗黑模式。 这里对View就不多谈了,这部分View比较容易。

2.获取Data

一言部件的数据如何获取?从哪来? 目前iWidget采取的方案是通过Hitokoto提供的接口来获取对应的数据。

不得不说,Hitokoto的接口很方便有质量。


import Foundation
struct OneWord {
let content: String
let length: Int
}
struct OneWordLoader {
static func fetch(completion: @escaping (Result<OneWord, Error>) -> Void) {
let oneWordURL = URL(string: "https://v1.hitokoto.cn/")!
let task = URLSession.shared.dataTask(with: oneWordURL) { (data, response, error) in
guard error == nil else {
completion(.failure(error!))
return
}
let oneWord = getOneWordInfo(fromData: data!)
completion(.success(oneWord))
}
task.resume()
}
static func getOneWordInfo(fromData data: Foundation.Data) -> OneWord {
    let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
    let content = json["hitokoto"] as! String
    let length = json["length"] as! Int
    return OneWord(content: content, length: length)
}

}

其中`OneWord `声明了解析过后的数据类型,通过`OneWordLoader`的`fetch`方法请求API获取JSON数据后通过内部的`getOneWordInfo`方法解析JSON数据返回`OneWord`数据。
> 这部分代码我封装到了iWidget/Data/OneWordData.swift中,具体可见[GitHub](https://github.com/Littleor/iWidget)。 

## 3.编写Provider 
> Provider的作用主要用于控制Widget的刷新
一言的Provider中,我们需要在获取API数据后再给Widget刷新显示,故大概流程为: 获取数据->获取成功后刷新数据。

struct OneWordProvider: IntentTimelineProvider { public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (OneWordEntry) -> ()) { let entry = OneWordEntry(date: Date(),data: OneWord(content: "一言", length: 2)) completion(entry) }

public func timeline(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    let currentDate = Date()
    let refreshDate = Calendar.current.date(byAdding: .minute, value: 60, to: currentDate)!

    OneWordLoader.fetch { result in
        let oneWord: OneWord
        if case .success(let fetchedData) = result {
            oneWord = fetchedData
        } else {
            oneWord = OneWord(content: "获取失败", length: 4)
        }
        let entry = OneWordEntry(date: currentDate,data: oneWord)
        let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
        completion(timeline)
    }
}

}

其中

`let timeline = Timeline(entries: [entry], policy: .after(refreshDate))`

entries提供了下次更新的数据,policy提供了下次更新的时间。

其中`policy`可填`.never`永不更新(可通过`WidgetCenter`更新)、`.after(Date)`指定多久之后更新、`.atEnd`指定Widget通过你提供的entries的Date更新。

# 预览的坑 
## 介绍 
> 个人被这个坑卡了很久,后来看了WidgetKit的代码才找到原因,这个真心坑。
当使用预览的时候编译会报错:

reference to invalid associated type 'Entry' of type 'Provider'

这是因为你启用了预览:

struct MainWidget_Previews: PreviewProvider { static var previews: some View { PayToolsEntryView(entry: SimpleEntry(date: Date())) .previewContext(WidgetPreviewContext(family: .systemSmall)) } }

## 解决方案
对于这个问题我目前只想到了2个办法解决:
### 1.直接注释预览 
在编译前注释Preview的代码即可,需要预览再解除

### 2.Provider添加typealias 
直接在Provider中添加:

typealias Entry = SimpleEntry


其中SimpleEntry需要使用你的Entry的变量名替换。

# 后记 
这一次大概整理了下自己开发一个Widget的大概过程,还有可配置小部件和小部件Link等操作下次再分享,敬请期待。

完整代码见[GitHub](https://github.com/Littleor/iWidget)

后续还会慢慢完善WidgetKit开发的文章,同时[iWiget](https://github.com/Littleor/iWidget)也会不断完善,这篇文章对你有用就点个Star吧!

项目地址: https://github.com/Littleor/iWidget

#iOS, Widget

作者: Littleor

版权: 除特别声明,均采用BY-NC-SA 4.0许可协议,转载请表明出处

目录Content

已有 4 条评论

  1. TF版本没找到一言组件

    1. iWidget TF版本有哦

      1. 打开app的时候有,但是在桌面上添加的时候真的没找到,只有六个,分别是 支付 倒计时 两个RSS 成语 单词

        1. 请使用 部件中心,iWidget的一言暂时被注释了

评论