Prática: Criando uma Extensão de Atividades Diárias do Traqt
Assista e acompanhe a prática em sala dos exercícios "Criando uma Extensão de Atividades Diárias do Traqt". Execute as instruções conforme a apresentação.
Exercício 1. Incluindo uma Extensão no Projeto
1.1. Adicionar um Target de Today Extension ao Projeto
Adicionar um Target do tipo Today Extension ao projeto com o Product Name DailyActivitiesExtension.
1.2. Alterar o Bundle Display Name da Extensão
Alterar a chave Bundle display name no arquivo Info.plist da extensão para o nome Atividades do Dia.
Exercício 2. Criando a Interface da Extensão
2.1. Abra o arquivo Main.storyboard do Grupo Daily Activities Extension
2.2. Exclua o View Controller criado por padrão
2.3. Adicione um Table View Controller ao Storyboard
2.4. Configure a Prototype Cell
Configure a Prototype Cell do View Controller de acordo com as informações abaixo:
- Style: Right Detail
- Identifier: ActivityCell
- Selection: None
- Background: Clear Color
2.5. Configure o Label do Prototype Cell
Modifique a propriedade Color do Label Title da Prototype Cell para White.
2.6. Modifique o Background Color dos Demais elementos
Modifique a propriedade Background Color no Table View e no Content View para Clear Color
2.7. Configure o Table View Controller como ponto de entrada
Exercício 3. Codificando a Extensão
3.1. Atualize o código do Arquivo TodayViewController.swift
Atualize o código do Arquivo TodayViewController.swift conforme a listagem abaixo:
class TodayViewController: UIViewController, NCWidgetProviding {class TodayViewController: UITableViewController, NCWidgetProviding { // // MARK: - Properties // // MARK: - View Lifecycle override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view from its nib. }override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. }// // MARK: - NCWidgetProviding func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {// Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResult.Failed // If there's no update required, use NCUpdateResult.NoData // If there's an update, use NCUpdateResult.NewDatacompletionHandler(NCUpdateResult.NewData) } }
3.2. Inclua as Propriedades na Classe
Inclua as seguintes propriedades na classe:
let CELL_HEIGHT = CGFloat(44)
var activities = [
["Activity": "Atividade 1", "Time": "07:00"],
["Activity": "Atividade 2", "Time": "08:30"],
["Activity": "Atividade 3", "Time": "14:45"],
]
3.3. Inclua uma Seção de Métodos de Apoio
Inclua uma seção de métodos de apoio conforme a listagem abaixo:
//
// MARK: - Métodos de Apoio
func calculatePreferredContentSize() {
let totalHeight = CGFloat(self.activities.count) * CELL_HEIGHT + self.tableView.sectionFooterHeight
self.preferredContentSize = CGSize(width: 0, height: totalHeight)
}
3.4. Atualize o Código do método widgetPerformUpdateWithCompletionhandler:completionHandler:
Atualize o Código do método widgetPerformUpdateWithCompletionhandler:completionHandler: conforme a listagem abaixo:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) { self.calculatePreferredContentSize() completionHandler(NCUpdateResult.NewData) }
3.5. Inclua os Métodos do UITableViewDataSource e UITableViewDelegate
Inclua os Métodos do UITableViewDataSource e UITableViewDelegate conforme a listagem abaixo:
//
// MARK: - UITableViewDelegate
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return CELL_HEIGHT
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ActivityCell", forIndexPath: indexPath)
cell.textLabel?.text = self.activities[indexPath.row]["Activity"]
cell.detailTextLabel?.text = self.activities[indexPath.row]["Time"]
return cell
}
3.6. Configure a propriedade Class para TodayViewController
Exercício 4. Compartilhando os Dados de Atividades
4.1. Configure o App Groups para o Target do App
Usar o App Group: group.com.catteno.traqt.extensions.
4.2. Configure o App Groups para o Target da Extension
Usar o App Group: group.com.catteno.traqt.extensions.
4.3. Crie o arquivo SharedActivity.swift
Crie o arquivo do tipo Swift File com o nome SharedActivity.swift no grupo DailyActivityExtension, selecionando os Targets do App e da Extensão.
4.4. Inclua o código do novo arquivo
Inclua o código do novo arquivo conforme a listagem abaixo:
let kSharedActivityFieldId = "ActivityId"
let kSharedActivityFieldName = "Name"
let kSharedActivityFieldDaysOfWeek = "DaysOfWeek"
let kSharedActivityFieldTimeOfDay = "TimeOfDay"
/**
Um classe para representar as informações de uma atividade que são compartilhadas entre o Target do App e a extensão Today.
*/
class SharedActivity: NSObject {
//
// MARK: - Properties
var activityId: String
var name: String
var daysOfWeek: NSNumber
var timeOfDay: NSNumber
var timeOfDayDesc: String {
return self.timeOfDay.integerValue.timeIntToString()
}
//
// MARK: - Initializers
init(activityId: String, name: String, daysOfWeek: Int, timeOfDay: Int) {
self.activityId = activityId
self.name = name
self.daysOfWeek = daysOfWeek
self.timeOfDay = timeOfDay
super.init()
}
init(info: NSDictionary) {
self.activityId = info[kSharedActivityFieldId] as! String
self.name = info[kSharedActivityFieldName] as! String
self.daysOfWeek = info[kSharedActivityFieldDaysOfWeek] as! NSNumber
self.timeOfDay = info[kSharedActivityFieldTimeOfDay] as! NSNumber
super.init()
}
}
4.5. Adicione os Arquivos do Grupo Extensions no Target da Extension
4.6. Adicione os Arquivos WeekDay.swift e WeekDays.swift no Target da Extension
4.7. Inclua o método de conversão no arquivo Acitvity+Reminders.swift
Inclua o método para converter uma Activity em um SharedActivity no arquivo Activity+Reminders.swift, conforme a listagem abaixo:
//
// MARK: - Métodos para gerenciar o armazenamento compartilhado
func toSharedActivityDictionary() -> NSDictionary {
let activityData = NSMutableDictionary()
activityData[kSharedActivityFieldId] = self.activityId
activityData[kSharedActivityFieldName] = self.name
activityData[kSharedActivityFieldDaysOfWeek] = self.reminderDays
activityData[kSharedActivityFieldTimeOfDay] = self.reminderTime
return activityData
}
4.8. Inclua o método para atualizar o armazenamento compartilhado
Inclua o método para atualizar o armazenamento compartilhado após a declaração do método anterior:
func updateSharedStorage() {
let sharedUserDefaults = NSUserDefaults(suiteName: "group.com.syligo.traqt.extensions")!
var activities: [NSDictionary]
if var sharedActivities = sharedUserDefaults.objectForKey("SharedActivities") as? [NSDictionary] {
// First search for the activity in the list to remove
var activityIndex = -1
for var i = 0; i < sharedActivities.count; i++ {
let activity = SharedActivity(info: sharedActivities[i])
if activity.activityId == self.activityId {
activityIndex = i
break
}
}
if activityIndex >= 0 {
sharedActivities.removeAtIndex(activityIndex)
}
activities = sharedActivities
} else {
// If no dictionary exists before create a new one
activities = [NSDictionary]()
}
// Add the activity to the shared activity list
if !deleted && reminders?.boolValue ?? false {
activities.append(self.toSharedActivityDictionary())
}
sharedUserDefaults.setObject(activities, forKey: "SharedActivities")
sharedUserDefaults.synchronize()
}
4.9. Atualize o método didSave
Atualziar o método didSave no arquivo Activity+Reminders.swift conforme a listagem abaixo:
override func didSave() { super.didSave() // Remove as notificações agendadas anteriormente Activity.clearNotificationsOfActivity(self.activityId) self.updateSharedStorage() // Verifica se o registro da atividade não esta sendo excluido, nesse caso o usuário não desejará mais receber notificação alguma if !deleted { scheduleLocalNotifications() } }
Exercício 5. Carregando as Atividades do Dia
5.1. Atualizar as propriedades da Classe TodayViewConroller
Atualize as propriedades da Classe TodayViewController conforme o código abaixo:
// // MARK: - Properties let CELL_HEIGHT = CGFloat(44) var sharedUserDefaults = NSUserDefaults(suiteName: "group.com.catteno.traqt.extensions") var activities = [SharedActivity]()var activities = [ ["Activity": "Atividade 1", "Time": "07:00"], ["Activity": "Atividade 2", "Time": "08:30"], [""Activity": "Atividade 3", "Time": "14:45"], ]
5.2. Atualizar a implementação do método tableView:cellForRowAtIndexPath:
Atualize a implementação do método tableView:CellForRowAtIndexPath: conforme a listagem abaixo:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("ActivityCell", forIndexPath: indexPath) as UITableViewCellcell.textLabel?.text = self.activities[indexPath.row]["Activity"]cell.textLabel?.text = self.activities[indexPath.row].namecell.detailTextLabel?.text = self.activities[indexPath.row]["Time"]cell.detailTextLabel?.text = self.activities[indexPath.row].timeOfDayDesc return cell }
5.3. Incluir o método de apoio para Recarregar os Dados
Inclua o método de apoio conforme a listagem:
func refreshData() {
self.activities.removeAll(keepCapacity: true)
if let sharedActivities = sharedUserDefaults?.objectForKey("SharedActivities") as? [NSDictionary] {
let refTime = NSDate().toIntTime()
for activityDictionary in sharedActivities {
let activity = SharedActivity(info: activityDictionary)
let weekDays = WeekDays(rawValue: activity.daysOfWeek.integerValue)
// Filter by time of the day and day of the week
if activity.timeOfDay.integerValue >= refTime && (weekDays.contains(NSDate().weekDay.asWeekDays())) {
activities.append(activity)
}
}
}
}
5.4. Atualizar a implementação do método widgetPerformUpdateWithCompletionHandler:
Atualize a implementação do método widgetPerformUpdateWithCompletionHandler: conforme a listagem abaixo, exclua o código:
self.calculatePreferredContentSize()
completionHandler(NCUpdateResult.NewData)
E inclua o seguinte código:
self.refreshData()
if activities.count > 0 {
completionHandler(NCUpdateResult.NewData)
// Remove o Label informando a ausência de atividades
self.tableView.tableHeaderView = nil
self.calculatePreferredContentSize()
self.tableView.reloadData()
} else {
completionHandler(NCUpdateResult.NoData)
// Adiciona uma Label informando que não há atividades
let label = UILabel()
label.textColor = UIColor.whiteColor()
label.text = "Nenhuma atividade pendente."
label.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: label.intrinsicContentSize().height + 20)
self.tableView.tableHeaderView = label
self.preferredContentSize = CGSize(width: 0, height: label.intrinsicContentSize().height + 20)
}