个人开发Apple Watch应用-“土味情话”

Apple Watch fetch网站API获取json数据

Posted by PhDLuffy on August 3, 2021

个人开发Apple Watch应用-“土味情话”

既然没有人愿意站出来唱唱反调,那我来吧。

—— 卢娜·洛夫古德

应用交互界面

应用交互界面非常简单,

一个VStack下面放一个“土味情话”标题Text和一个List,List下面放一个QinghuaView显示土味情话的内容。

List下面这个QinghuaView接收网络API的json数据并显示到一个Text里,同时在QinghuaView下的VStack下包含这个Text和一个Button,Button的作用是点击重新运行数据获取函数,刷新情话内容。

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading){
            Text("土味情话")
            List {
                QinghuaView().environmentObject(DataStore())
            }
        }    
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

QinghuaView.swift

var body: some View {
        VStack{
            Spacer()
            Text("\(qinghuaData.qinghuaneirong)")
            Button(""){
                self.lovelettergetData()
            }
        }  
    }
}

struct QinghuaView_Previews: PreviewProvider {
    static var previews: some View {
        QinghuaView()
    }
}

fetch网站API获取json数据

建立网站API的json数据结构体

举个栗子:

我的自建网站的API-qinghua,一个静态API,JSON数据结构如图1

结构为两层,一层是”code”,”msg”,”newslist”,我们需要的是newslist这一层下的信息,newslist下是一个content数组,所以我们的结构体应该这样写。

//第一个struct是第一层级的数据结构,我们只要newslist这一级的内容

struct LoveletterData: Decodable {
	var loveletter: [LoveletterFirstLayerData]
	
	enum CodingKeys: String, CodingKey {
		case lovetetter = "newslist"
	}
}

//第二个struct是第二层级的数据结构,newslist这一级里面只有一个content数组

struct LoveletterFirstLayerData: Decodable {
	var content: String
}

图1

JSON数据结构写完之后,我们写获取网络数据function

我们要获得API中的json数据

func lovelettergetData() {
	let urlString = "网站API的json数据请求地址"
	let url = URL(string: urlString)
	
	URLsession.shared.dataTask(with: url!) {data, _, error in
		DispatchQueue.main.async {
			var success = false
			if let loveletterdata = data {
				if let decodedloveletter = try?
					JSONDecoder().decode(LoveletterData.self, from: loveletterdata){
					self.qinghuaData.qinghuaneirong = 
						decodedloveletter.loveletter[Int.random(in: 0...20)].content
					success = true
					NSLog("\(success)")
				}
			}
			if !success {
					NSLog("webdata: no data found")
			}
		}
	}.resume()
}

我们获取到了网站JSON数据,赋值给了qinghuaneirong这个变量,我们需要将这个变量设为全局变量

新建一个swift文件:DataStore.swift文件

这里我们建立了一个类,DataStore,里面有一个全局变量qinghuaneirong

import Foundation

class DataStore: ObservableObject{
	@Published var qinghuaneirong: String = ""
}

我们在lovelettergetData()这个function里面建立一个变量,类型是DataStore

@environmentObject var qinghuaData:DataStore

这样的话,我们就能将API的json获取的内容赋予变量self.qinghuaData.qinghuaneirong了

在QinghuaView里,我们就可以引用这个变量的内容

Text("\(qinghuaData.qinghuaneirong)")
Button(""){
	self.lovelettergetData()
}

在ContentView中引用上面这个QinghuaView时候,还需要注意,我们要使用.environmentObject来引用,这样才能做到随时更新内容

QinghuaView().environmentObject(DataStore())

Complication复杂功能

显示界面非常简单,上方是标题“土味情话”,下方是我自己定制的显示内容,樱木花道的口头禅“我是天才”

Complication复杂功能这一块,最初的设想是在表盘主界面随时间显示变化不同的情话内容,在数据交互上卡壳了一段时间,最近终于能把数据传进complication了,但是最终的显示效果并不好,内容字体会被apple watch系统放大,最多显示几个字,不能显示一整段内容,没法达到我想要的效果,应该是apple watchos7目前的系统限制,期望在未来能够开放相应的接口达到我最初想要的效果。

(A) complication复杂功能的模板内容设置

也就是当你在表盘复杂功能页面选择不同程序的complication默认的显示效果,在码代码之前,如图2需要先去这个位置,勾选需要用到的complication families,我目前只用到了Modular Large

图2

在complicationController.swift下

func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
        
        switch complication.family {

           case .modularLarge:
               let template = CLKComplicationTemplateModularLargeTallBody()
               template.headerTextProvider = CLKSimpleTextProvider(text: "土味情话")
               template.bodyTextProvider = CLKSimpleTextProvider(text: "内容")
               handler(template)
               
           default:
               preconditionFailure("Complication family not supported")
           }
        
    }

getLocalizableSampleTemplate就是相应的模板,我们选择modularLarge这个family成员,template选择ModularLargeTallBody这个,具体的显示布局,苹果开发官网有详细的展示,这个比较适合我的程序。

headerTextProvider和bodyTextProvider是这个template所强制要求的,我们填入内容即可,text也要用它规定的CLKSimpleTextProvider来进行赋值。

(B) Complication表盘界面初始化

func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
        
        let entry: CLKComplicationTimelineEntry
        let neirong: String
        
        neirong = "我是天才"
        
        switch complication.family {
           
               
           case .modularLarge:
               let template = CLKComplicationTemplateModularLargeTallBody()
               template.headerTextProvider = CLKSimpleTextProvider(text: "土味情话")
               template.bodyTextProvider = CLKSimpleTextProvider(text: neirong)
               entry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
        
           default:
               preconditionFailure("Complication family not supported")
           }
           handler(entry)
    }

getCurrentTimelineEntry是程序在表盘的显示内容,本来我想在这里进行数据交互,让neirong能够随着程序按钮点击更换,发现这里的数据和程序里面的数据交互之间有问题,无法做到实时交互,查阅相关资料发现是watchos系统有限制,再加上我发现即使数据传过来,显示效果也不佳,所以就放弃了在这里进行数据交互,直接赋值,甚至我们直接将模板复制过来也可以。

这里记得在最后加一个handler(entry)

我尝试的数据传递方法是用UserDefaults,将变量付给这样的数据结构的变量即可,但是我发现数据更新不及时

(C) Complication刷新

在ExtensionDelegate.swift下

这里好像是一些能够调用Complication的东西,先建一个刷新Complication的function

func reloadActiveComplications() {
        let server = CLKComplicationServer.sharedInstance ()
     
        for complication in server.activeComplications ?? [] {
            server.reloadTimeline (for: complication)
        }
    }

这个function执行会刷新目前所有的Complication

在applicationDidFinishlaunching这个function下,我们执行reloadActiveComplication这个function

self.reloadActiveComplications()

这样当我们按数码表冠回到表盘的时候,系统会重新刷新所有Complication

土味情话watch app最终的使用效果如下。

表盘界面

app界面,点按内容可以自动刷新

点击刷新内容

以上,我是@PhDLuffy,我们都有美好的未来。

感谢大家点赞、评论、分享、喜欢、收藏

赞赏~