Go and Fyne - Need response from dialog and enable/disable Submit button - go

Just starting to look at both Go and Fyne. After writing a test CRUD program using information obtained from examples, I need to solve the following:
I need an initial Dialog or similar in order to allow selection of an existing record or "new" to indicate a new record. My existing Dialog does not handle this adequately.
I need to be able to enable/disable the Submit button to allow or disallow update if the data has has-not changed.
The program compiles without error, however "go vet" and VsCode indicates (eg. for line):
{"Given Names:", arwWidgets[I_NDX_GIVEN]},
go vet shows:
ERROR: "unknown field 'Key' in struct literal of type widget.FormItem",
however, if I include a key, compiler fails.
Being new to both Go and Fyne, there are probably fundamental mistakes.
Test program is as follows:
package main
// Dependency: local directory "data".
import (
"errors"
"io/ioutil"
"log"
"os"
"strings"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/dialog"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
)
const BY_SEP byte = byte('|')
const I_ARRAY_LEN int = 10
const I_NDX_GIVEN int = 0
const I_NDX_FAMILY int = 1
const I_NDX_TITLE int = 2
const I_NDX_ADDR1 int = 3
const I_NDX_ADDR2 int = 4
const I_NDX_ADDR3 int = 5
const I_NDX_STATE int = 6
const I_NDX_ZIP int = 7
const I_NDX_TELE1 int = 8
const I_NDX_TELE2 int = 9
var _arsOldData [I_ARRAY_LEN]string
var _arsNewData [I_ARRAY_LEN]string
var _tfDataHasChanged bool
var _sKey string
var _arwEntryWidgets [I_ARRAY_LEN]*widget.Entry
var _wApp fyne.App
var _wWindow fyne.Window
var _wModal widget.PopUp
var _wFormKeyEntry widget.Form
//-------------------------------------------------------------------------------
func main() {
log.Println("In fytest01")
_, err := os.Stat("./data/")
if err != nil {
log.Println("You need to create directory: 'data'")
os.Exit(1)
}
//fnReadData()
_wApp := app.New()
_wWindow := _wApp.NewWindow("Customer Details")
//_wFormKeyEntry := fnCreateKeyForm()
wFormMain := fnCreateMainForm()
//_wModal := widget.NewModalPopUp(_wFormKeyEntry, _wWindow.Canvas())
_wWindow.Resize(fyne.NewSize(500, 400))
_wWindow.CenterOnScreen()
_wWindow.SetContent(widget.NewVBox(
wFormMain,
widget.NewGroup("",
fyne.NewContainerWithLayout(layout.NewGridLayout(2),
widget.NewButton("Exit", func() {
_wApp.Quit()
}),
widget.NewButton("Submit", func() {
log.Println("Submit button pressed")
err := fnUpdateData()
if err == nil {
dialog.ShowInformation("Information", "Data was updated", _wWindow)
wEntryForKey := widget.NewEntry()
wEntryForKey.SetPlaceHolder("Enter Key (4) : ")
wEntryForKey.OnChanged = func(sKey string) {
log.Println("Entered", sKey)
if len(sKey) == 4 {
_sKey = sKey
}
if len(_sKey) == 4 {
dialog.ShowInformation("Information", "Key "+_sKey+" has been entered - press OK", _wWindow)
fnReadData()
fnRefreshWidgetData()
}
}
dialog.ShowCustom("Enter Customer Key", "OK", wEntryForKey, _wWindow)
} else {
dialog.ShowError(err, _wWindow)
}
}),
))),
)
//fnShowKeyEntryDialog()
wEntryForKey := widget.NewEntry()
wEntryForKey.SetPlaceHolder("Enter Key (4) : ")
wEntryForKey.OnChanged = func(sKey string) {
log.Println("Entered", sKey)
if len(sKey) == 4 {
_sKey = sKey
}
if len(_sKey) == 4 {
dialog.ShowInformation("Information", "Key "+_sKey+" has been entered - press OK", _wWindow)
fnReadData()
fnRefreshWidgetData()
}
}
dialog.ShowCustom("Enter Customer Key", "OK", wEntryForKey, _wWindow)
_wWindow.ShowAndRun()
//_wModal.Show()
}
//-------------------------------------------------------------------------------
func fnCreateAllEntryWidgets() {
_arwEntryWidgets[I_NDX_GIVEN] = fnCreateSingleEntryWidget(I_NDX_GIVEN)
_arwEntryWidgets[I_NDX_FAMILY] = fnCreateSingleEntryWidget(I_NDX_FAMILY)
_arwEntryWidgets[I_NDX_TITLE] = fnCreateSingleEntryWidget(I_NDX_TITLE)
_arwEntryWidgets[I_NDX_ADDR1] = fnCreateSingleEntryWidget(I_NDX_ADDR1)
_arwEntryWidgets[I_NDX_ADDR2] = fnCreateSingleEntryWidget(I_NDX_ADDR2)
_arwEntryWidgets[I_NDX_ADDR3] = fnCreateSingleEntryWidget(I_NDX_ADDR3)
_arwEntryWidgets[I_NDX_STATE] = fnCreateSingleEntryWidget(I_NDX_STATE)
_arwEntryWidgets[I_NDX_ZIP] = fnCreateSingleEntryWidget(I_NDX_ZIP)
_arwEntryWidgets[I_NDX_TELE1] = fnCreateSingleEntryWidget(I_NDX_TELE1)
_arwEntryWidgets[I_NDX_TELE2] = fnCreateSingleEntryWidget(I_NDX_TELE2)
}
//-------------------------------------------------------------------------------
func fnCreateSingleEntryWidget(iNdxData int) *widget.Entry {
wEntry := widget.NewEntry()
wEntry.SetText(_arsOldData[iNdxData])
wEntry.OnChanged = func(sText string) {
_arsNewData[iNdxData] = sText
fnCheckIfDataHasChanged()
}
return wEntry
}
//-------------------------------------------------------------------------------
func fnCreateFormFields(arwWidgets [I_ARRAY_LEN]*widget.Entry) []*widget.FormItem {
return []*widget.FormItem{
{"Given Names:", arwWidgets[I_NDX_GIVEN]},
{"Family Name: ", arwWidgets[I_NDX_FAMILY]},
{"Title: ", arwWidgets[I_NDX_TITLE]},
{"Address Ln 1: ", arwWidgets[I_NDX_ADDR1]},
{" '' Ln 2: ", arwWidgets[I_NDX_ADDR2]},
{" '' Ln 3: ", arwWidgets[I_NDX_ADDR3]},
{" '' State ", arwWidgets[I_NDX_STATE]},
{" '' Zip: ", arwWidgets[I_NDX_ZIP]},
{"Telephone 1: ", arwWidgets[I_NDX_TELE1]},
{"Telephone 2: ", arwWidgets[I_NDX_TELE2]},
}
}
//-------------------------------------------------------------------------------
func fnCheckIfDataHasChanged() {
var tfChanged bool = false
for iNdxData := 0; !tfChanged && iNdxData < len(_arsOldData); iNdxData++ {
tfChanged = (_arsNewData[iNdxData] != _arsOldData[iNdxData])
}
if tfChanged != _tfDataHasChanged {
_tfDataHasChanged = tfChanged
if tfChanged {
// COULD NOT CREATE _wBtnSubmitMain AS A GLOBAL VARIABLE.
//_wBtnSubmitMain.Show()
//_wBtnSubmitMain.Enable()
} else {
//_wBtnSubmitMain.Disable()
//_wBtnSubmitMain.Hide()
}
}
}
//-------------------------------------------------------------------------------
func fnReadData() {
_tfDataHasChanged = false
log.Println("fnReadData: Key = " + _sKey)
var sData string
if len(_sKey) > 0 {
arbData, _ := ioutil.ReadFile("./data/" + _sKey)
if arbData != nil {
sData = string(arbData)
}
}
log.Println("fnReadData: sData = " + sData)
/* CLEAR OLD DATA */
for iNdxData := 0; iNdxData < I_ARRAY_LEN; iNdxData++ {
_arsOldData[iNdxData] = ""
}
/* POPULATE DATA IF ANY */
var iNdx1 int = 0
var iNdxData int = 0
var iLen int = len(sData)
for iNdx2 := 0; iNdx2 < len(sData); iNdx2++ {
if sData[iNdx2] == BY_SEP {
_arsOldData[iNdxData] = sData[iNdx1:iNdx2]
iNdx1 = iNdx2 + 1
iNdxData++
} else if iNdx2 == (iLen - 1) {
_arsOldData[iNdxData] = sData[iNdx1 : iNdx2+1]
}
}
for iNdx := 0; iNdx < I_ARRAY_LEN; iNdx++ {
_arsNewData[iNdx] = _arsOldData[iNdx]
}
}
//-------------------------------------------------------------------------------
func fnUpdateData() error {
if !_tfDataHasChanged {
return errors.New("Data has not changed")
}
if len(_sKey) < 1 {
_sKey = "9999"
//dialog.ShowInformation("Information", "Default key of 9999 was used", _wWindow) // CAUSES ERROR
}
var sNewData string = ""
for iNdxData, sVal := range _arsNewData {
if strings.Index(sVal, "|") >= 0 {
sVal = strings.ReplaceAll(sVal, "|", ":")
}
if iNdxData != I_ARRAY_LEN-1 {
sNewData += sVal + string(BY_SEP)
} else {
sNewData += sVal
}
}
log.Println("New Data = " + sNewData)
var err error = ioutil.WriteFile("./data/"+_sKey, []byte(sNewData), 0644)
if err == nil {
for iNdxData := 0; iNdxData < len(_arsNewData); iNdxData++ {
_arsOldData[iNdxData] = _arsNewData[iNdxData]
}
fnCheckIfDataHasChanged()
}
return err
}
//-------------------------------------------------------------------------------
func fnCreateKeyForm() *widget.Form {
wEntryKey := widget.NewEntry()
return &widget.Form{
Items: []*widget.FormItem{
{"Key:", wEntryKey}},
OnSubmit: func() {
_sKey = wEntryKey.Text
log.Println("Key = " + _sKey)
fnReadData()
log.Println("Data has been read")
fnRefreshWidgetData()
_wFormKeyEntry.Hide()
//_wModal.Hide()
},
}
}
//-------------------------------------------------------------------------------
//func fnCreateMainWindow() {
// log.Println("Creating Main Window Form")
// wFormMain := fnCreateMainForm()
// log.Println("Creating Main Window Window")
// _wWindow.SetContent( //widget.NewVBox(
// wFormMain,
// )
//}
//-------------------------------------------------------------------------------
func fnCreateMainForm() *widget.Form {
log.Println("Create form1 widgets")
fnCreateAllEntryWidgets()
log.Println("Create form1 FormFields")
arwFormFields := fnCreateFormFields(_arwEntryWidgets)
log.Println("Creating Form1")
return &widget.Form{
Items: arwFormFields,
}
}
//-------------------------------------------------------------------------------
func fnRefreshWidgetData() {
for iNdx := 0; iNdx < I_ARRAY_LEN; iNdx++ {
_arwEntryWidgets[iNdx].SetText(_arsNewData[iNdx])
}
}

Can you just add a "New" button to the start window which fills in defaults then opens the main window. Does that make sense?
You create the Submit button by creating and passing it directly to SetContent. You need to save it in a variable, so that you can at some point call Disable() on it.
submitButton := widget.NewButton("Submit", func() {
log.Println("Submit button pressed")
...
})
_wWindow.SetContent(widget.NewVBox(
wFormMain,
widget.NewGroup("",
fyne.NewContainerWithLayout(layout.NewGridLayout(2),
widget.NewButton("Exit", func() {
_wApp.Quit()
}),
submitButton,
))),
)
...
if dataChanged {
submitButton.Enable()
} else {
submitButton.Disable()
}
I think the go vet message is saying you should name all the fields in your struct literal (though I get a different message to you using Go 1.13). Go allows you to add the values by order or by name - go vet is telling you that by name is safer (eg if fields are moved or inserted to the FormItem struct then your code would be broken).
Ie:
return []*widget.FormItem{
{ Text: "Given Names:", Widget: arwWidgets[I_NDX_GIVEN]},
...
Also it may simplify things to put all your global variables into a struct and then you can have "methods" on the struct. (There are a few reasons to avoid globals which you probably already know. I appreciate that this probably started as a simple test that has grown but it may be a good time to refactor.)

To change button options on toggle you need to use something like this:
buttonTitle := "Disable"
button := widget.NewButton(buttonTitle, nil)
changeButton := func() {
// here could be your logic
// how to disable/enable button
if button.Text == "Disable" {
buttonTitle = "Enable"
//button.Disable()
}
button.SetText(buttonTitle)
button.Refresh()
}
button.OnTapped = changeButton

Related

How work history of web browser under the hood

I am trying to implement my own web browser history for WKWebView on iOS, but I can't implement this functionality completely, and each time I obtain trouble.
I can create a history where the user did be and then moving forward and backward inside history.
But I have next trouble, and I think it an only one of many problems on my way.
When I have a history with for example 10 elements, and then I am moving back to element number 5 and then go don't forward but try to open the new link I can't remove element 6-10 and put the new link.
I think my problem that I can't fully understand how history work inside all browsers under the hood, this is not a hard task but I am confused inside this algorithm.
My main data structure for holding history
Help me understand how to work this algorithm inside browsers or maybe exist a good theory about it?
I have solved this problem and realize the full algorithm well, the completed project available here: https://github.com/IhorYachmenov/Custom-browser-history-for-WKWebView.
Algorithm:
struct PlayerHistory {
static var shared = PlayerHistory()
var historyExist: Bool = false
var historyCurrentPosition: Int = 0
var historyLastPositionBeforeUpdatingHistory: Int!
var userHistoryKey: String!
var backPressed: Bool!
var forwardPressed: Bool!
var urlOfPlayer: String!
// Function only for first loading inside <viewDidLoad or another method from app LifeCycle>.
mutating func getUrlForFirstLoading(initURL: String, key: String) -> String {
urlOfPlayer = initURL
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
updateFirstElement(key: key, url: initURL)
return initURL
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return initURL
}
let position = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.count - 1
historyExist = true
historyCurrentPosition = position
userHistoryKey = key
let initUrlFromHistoryStorage = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.last!.url
return initUrlFromHistoryStorage
}
// Create new or update exist history, use this method indsede <decidePolicyForNavigation>.
mutating func updatePlayerHistory(backlisk: [String], key: String) {
var history = [WebViewHistory]()
for i in backlisk {
history.append(WebViewHistory(i))
}
if (historyExist == true) {
// If old history exist need compound both and then to save.
let oldHistory = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let oldAndNewHostoryTogether = oldHistory! + history
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(oldAndNewHostoryTogether, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
} else {
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
historyExist = true
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
}
}
// Before using this method check if result don't equals nil. Use this method for navigation beetween history
func moveThroughHistory(key: String, direction: Bool) -> String? {
guard historyExist != false else {
return nil
}
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
if (direction == true) {
let index = historyCurrentPosition + 1
guard index != history.count else { return nil }
return history[index].url
} else {
let index = historyCurrentPosition - 1
guard index > 0 else { return history[0].url }
return history[index].url
}
}
// Method <setCurrentPosition> each time set position at history
mutating func setCurrentPosition(url: String, key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else { return }
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else { return }
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let index = history?.firstIndex(of: WebViewHistory(url))
guard index != nil else {
historyCurrentPosition = 0
return
}
historyCurrentPosition = index!
}
// <removeUnusedPeaceOfHistory> need use when user want open new page staying inside the middle of history
mutating func removeUnusedPeaceOfHistory(key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
return
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return
}
var history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let startIndex = historyCurrentPosition + 1
let endIndex = history.endIndex - 1
let countOfAllElements = history.count
guard startIndex != countOfAllElements else { return }
let range = startIndex...endIndex
history.removeSubrange(range)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
}
// Use <updateFirstElement> inside <getUrlForFirstLoading> if history doesn't exist
private mutating func updateFirstElement(key: String, url: String) {
var history = [WebViewHistory]()
history.insert(WebViewHistory(url), at: 0)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.saveHistory(keyValuePair)
historyExist = true
historyCurrentPosition = 0
}
// Use <webViewWillBeClosedSaveHistory> when WKWebView should be closed, if the user moves through history new position will be saved.
mutating func webViewWillBeClosedSaveHistory(key: String) {
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let currentPosition = historyCurrentPosition + 1
guard currentPosition != history.count else { return }
removeUnusedPeaceOfHistory(key: key)
}
}

Cannot decode Caesar-Encrypted messages

I have to write a function for school that decodes Caesar-Encrypted messages. Our teacher gave us the code that encodes the messages, and asked us to write one based on the givenb function that decodes any encrypted message. (The key/shift wasn't given but was explicitly not 3 as in the the encode example, we should figure out ourselves. With online tools i know the key is 17 but for the purpose ofthe question i chose the given example with the known shift of 3)
The given Function with the Shift 3:
func main () {
var textN string = "Wenn man jetzt diesen Text Cäsar-Codieren möchte, dann macht man das so!" // Variable was given by tacher
var textC string = "Zhqq pdq mhwdw glhvhq Whbw Fävdu-Frglhuhq pöfkwh, gdqq pdfkw pdq gdv vr!" // Variable was given by tacher
fmt.Println(textN)
encode(textN)
decode(textC)
}
// The given Function with the shift 3:
func encode (text string){
var ergebnis string
for _,w:=range(text){
if w>='A' && w<='Z' {
if w+3>'Z'{
ergebnis += string((w+3)-'Z'+'A')
} else {
ergebnis += string(w+3)
}
} else if w>='a' && w<='z'{
if w+3>'z' {
ergebnis += string((w+3)-'z'+'a')
} else {
ergebnis += string(w+3)
}
} else {
ergebnis += string(w)
}
}
fmt.Println(ergebnis)
}
// My decode funtion with the "backwards shift" 3:
func decode (text string) {
var ergebnis string
for _,w:=range(text){
if w >='A' && w <='Z' {
if w-3 < 'A' {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string((w-3)+'Z'-'A')
} else {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string(w-3)
}
} else if w>='a' && w<='z'{
if w-3 < 'a' {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string((w-3)+'z'-'a')
} else {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string(w-3)
}
} else{
ergebnis += string(w)
}
}
fmt.Println(ergebnis)
}
Now the problem is: textN isn't equal to decode(textC), my code seems to fail at lowercase letters that shifted back 3 letters don't become the decoded letters they should be. I saw this at "z" and "x" and i dont have a clue why. I tried highering/lowering the shift but that didn't work, changed plusses and minusses and bigger lower signs. I don't know what to try and am thankful in advance.
there is one solution you can use, i can provide other slower one, but more interesting if you want.
func Chipher(input string, shift int) string {
bts := []rune(input)
var cursor *rune
// i em using it like this to remove repetition but its little advanced
// it may make you suspicious to teacher
shiftFn := func(start, end rune) bool {
r := *cursor
// not in range we cannot shift it
if r < start || r > end {
return false
}
res := start + (r-start+rune(shift))%(end-start)
if res < start {
res += end - start + 1
}
*cursor = res
return true
}
for i := range bts {
cursor = &bts[i]
// this is little trick, if one of expresions returns true, expressions
// after will not get executed as result would be true anyway
_ = shiftFn('a', 'z') || shiftFn('A', 'Z') || shiftFn('0', '9')
}
return string(bts)
}
now this is all beautiful but we have to do tests as well
func TestChipher(t *testing.T) {
testCases := []struct {
desc string
input, output string
shift int
}{
{
desc: "simple shift",
input: "abcd",
shift: 1,
output: "bcde",
},
{
desc: "negative shift",
input: "abcd",
shift: -1,
output: "zabc",
},
{
desc: "numbers",
input: "0123",
shift: 1,
output: "1234",
},
{
desc: "capital letters",
input: "ABCD",
shift: 1,
output: "BCDE",
},
{
desc: "big shift",
input: "ABCD",
shift: 1000,
output: "ABCD",
},
}
for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
res := Chipher(tC.input, tC.shift)
if res != tC.output {
t.Errorf("\n%s :result\n%s :expected", res, tC.output)
}
})
}
}
hope you will learn new things from this

MacCatalyst VPN Connection Ask Password Each Connection

When we try to connect with MacCatalyst, the system asks for a password every connection, but when working on a project created for only MacOS, it connects directly without asking for a password. The output of the “set” functions in the KeychainWrapper class is the same for both projects, but when I compare both keys in keychain access, there are differences as show in the attachment and my codes
Keychain Screenshot
Here is my IKEv2 connection codes:
public func connectIKEv2(config: Configuration, onError: #escaping (String)->Void) {
let p = NEVPNProtocolIKEv2()
p.authenticationMethod = NEVPNIKEAuthenticationMethod.none
p.deadPeerDetectionRate = NEVPNIKEv2DeadPeerDetectionRate.medium
p.disableRedirect = false
p.enableRevocationCheck = false
p.enablePFS = false
p.useExtendedAuthentication = true
p.remoteIdentifier = config.server
p.useConfigurationAttributeInternalIPSubnet = false
p.serverAddress = config.server
p.username = config.account
p.passwordReference = config.getPasswordRef()
loadProfile { _ in
self.manager.protocolConfiguration = p
self.manager.onDemandRules = [NEOnDemandRuleConnect()]
self.manager.isOnDemandEnabled = true
self.manager.isEnabled = true
self.saveProfile { success in
if !success {
onError("Unable to save vpn profile")
return
}
else {
print("Mayank: Profile saved")
}
self.loadProfile() { success in
if !success {
onError("Unable to load profile")
return
}
let result = self.startVPNTunnel()
if !result {
onError("Can't connect")
}
else {
print("Mayank: connecting with result")
print(result)
}
}
}
}
}
Here is KeychainWrapper set functions:
#discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
if let data = value.data(using: .utf8) {
return set(data, forKey: key, withAccessibility: accessibility)
} else {
return false
}
}
#discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
keychainQueryDictionary[SecValueData] = value
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
} else {
keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue
}
let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
if status == errSecSuccess {
return true
} else if status == errSecDuplicateItem {
return update(value, forKey: key, withAccessibility: accessibility)
} else {
return false
}
}
private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
let updateDictionary = [SecValueData:value]
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
}
let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
if status == errSecSuccess {
return true
} else {
return false
}
}

Algorithm: check for duplication in Swift array

Given an array of integers nums and an integer k. Find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the difference between i and j is at most k.
It is supposed to give me true, but it gives me false.
Any help, I appreciate it. Thank you so much.
class Solution
{
func containsNearbyDuplicate (nums: [Int], _ k: Int) -> Bool
{
var dict = [Int:Int]()
for i in 0..<nums.count
{
if dict[nums[i]] != nil
{
if dict.values.contains(nums[i]) && (i - dict[nums[i]]! <= k)
{
return true
}
else
{
dict[i] = nums[i]
}
}
}
return false
}
}
let test1 = Solution()
//var haha = [1,2,1,5,6,7,6,8,7,5]
//var haha = [1]
//var haha = [1,2]
//var haha = [1,2,3,5,6,8]
var haha = [-1,-1]
var result = test1.containsNearbyDuplicate(haha,1)
print(result)
You never add anything to dict:
func containsNearbyDuplicate (nums: [Int], _ k: Int) ->Bool
{
var dict = [Int:Int]()
for i in 0..<nums.count
{
if dict[nums[i]] != nil // This prevents anything to be added to dict
{
if dict.values.contains(nums[i]) && (i - dict[nums[i]]! <= k)
{
return true
}
else
{
dict[i] = nums[i] // This is never executed because of the above if above
}
}
}
return false
}
Try this one:
class Solution {
func containsNearbyDuplicate (nums: [Int], _ k: Int) ->Bool {
var dict = [Int:Int]()
for i in 0..<nums.count {
if let firstIndex = dict[nums[i]] where i - firstIndex <= k {
return true
}
dict[nums[i]] = i
}
return false
}
}

Why is one label updating quicker then the other?

I am making my own Spotify controller to learn how to interact with AppleScript and Swift (using Objective-C as a bridge) to make a bigger project later on. The only issue is the current progress time seems to update slightly faster then the time left.
This is what my application looks like:
The time on the left seems to update slightly faster then the time on the right, this means it is not accurate and can be bit strange to look at. How would I be able to fix this so both the progress time and time left update at the same time?
Here is my code:
ViewController.swift
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var statusLabel: NSTextField!
#IBOutlet weak var songProgress: NSProgressIndicator!
#IBOutlet weak var lblCurrentProgress: NSTextField!
#IBOutlet weak var lblTrackLength: NSTextField!
//let debugger = Debug(appName:"Spotify Menu", debugable: )
var previousScript:String = ""
var toggleScript:String = ""
var nextScript:String = ""
var statusScript:String = ""
var artistScript:String = ""
var songNameScript:String = ""
var trackPosScript:String = ""
var trackLengthScript:String = ""
var trackIDscript:String = ""
var trackName:String = "unknown"
var artistName:String = "unknown"
var playerStatus:String = "unknown"
var trackID:String = "unknown"
var playerPosistion:Double = -1.0
var trackLength:Double = -1.0
var percentThrough:Double = -1.0
let timeFormatter = twoDiffrentTimes()
func main()
{
getStatus()
getSongName()
getArtist()
getTrackLength()
getPlayerPos()
getID()
percentThrough = (playerPosistion/trackLength)*100
if(playerStatus == "playing" && trackName != "unknown" && artistName != "unknown")
{
statusLabel.stringValue = "Spotify is currently playing the song \"\(trackName)\" by \"\(artistName)\""
} else if(playerStatus == "paused"){
statusLabel.stringValue = "Spotify is currently paused"
} else if(playerStatus == "stopped") {
statusLabel.stringValue = "Spotify is not playing any music"
} else {
statusLabel.stringValue = "There is currently an error"
}
//NSLog("The song is currently \(percentThrough.toDecimalPlace("2"))% the way in")
songProgress.doubleValue = percentThrough
timeFormatter.update(playerPosistion, timeTwo: trackLength-playerPosistion)
lblCurrentProgress.stringValue = timeFormatter.timeOneString
lblTrackLength.stringValue = timeFormatter.timeTwoString
}
override func viewDidLoad() {
super.viewDidLoad()
previousScript = readFile("previousTrack")
if(previousScript == "")
{
NSLog("Error finding previous track script")
exit(1)
}
toggleScript = readFile("playPause")
if(toggleScript == "")
{
NSLog("Error finding toggle script")
exit(1)
}
nextScript = readFile("nextTrack")
if(nextScript == "")
{
NSLog("Error going forwared a track")
exit(1)
}
statusScript = readFile("playerStatus")
if(statusScript == "")
{
NSLog("Error going back a track")
statusLabel.stringValue = "Spotify is in an unknown status"
exit(1)
}
artistScript = readFile("getArtist")
if(artistScript == "")
{
NSLog("Cannot get artist script")
exit(1)
}
songNameScript = readFile("getSongName")
if(songNameScript == "")
{
NSLog("Cannot get song name script")
exit(1)
}
trackPosScript = readFile("playerPosition")
if(trackPosScript == "")
{
NSLog("Error getting track posistion script")
exit(1)
}
trackLengthScript = readFile("durationOfSong")
if(trackLengthScript == "")
{
NSLog("Error getting track length script")
exit(1)
}
trackIDscript = readFile("trackID")
if(trackIDscript == "")
{
NSLog("Error getting")
}
main()
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: Selector("main"), userInfo: nil, repeats: true)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func previous(sender: AnyObject)
{
runAppleScript(previousScript)
}
#IBAction func toggle(sender: AnyObject)
{
runAppleScript(toggleScript)
}
#IBAction func next(sender: AnyObject)
{
let nextFile = readFile("nextTrack")
if(nextFile == "")
{
NSLog("Error going forwared a track")
} else {
runAppleScript(nextFile)
}
}
func getPlayerPos()
{
playerPosistion = runAppleScript(trackPosScript).toDouble()
}
func getTrackLength()
{
trackLength = runAppleScript(trackLengthScript).toDouble()
}
func getSongName()
{
trackName = runAppleScript(songNameScript)
}
func getArtist()
{
artistName = runAppleScript(artistScript)
}
func getID()
{
trackID = runAppleScript(trackIDscript)
}
func getStatus()
{
let resultData = runAppleScript(statusScript)
if(resultData == "playing" || resultData == "paused" || resultData == "stopped")
{
playerStatus = resultData
} else {
playerStatus = "unknown"
}
}
}
classes.swift
import Foundation
class twoDiffrentTimes
{
var timeOne:Double = 0.0
var iMinuteOne:Int = 0
var iSecondsOne:Int = 0
var minuteOne:String = ""
var secondsOne:String = ""
var timeOneString:String = ""
var uSecondsOne:Double = 0.0
var temp:Int = 0
var timeTwo:Double = 0.0
var iMinuteTwo:Int = 0
var iSecondsTwo:Int = 0
var minuteTwo:String = ""
var secondsTwo:String = ""
var timeTwoString:String = ""
var uSecondsTwo:Double = 0.0
func update(timeOne:Double, timeTwo:Double)
{
//First Time
self.temp = Int(timeOne)
self.iMinuteOne = temp / 60
self.iSecondsOne = temp % 60
self.minuteOne = "\(iMinuteOne)"
if(self.iSecondsOne < 10)
{
self.secondsOne = "0\(self.iSecondsOne)"
} else {
self.secondsOne = "\(self.iSecondsOne)"
}
self.timeOneString = "\(self.iMinuteOne).\(self.secondsOne)"
//Second Time
self.temp = Int(timeTwo)
self.iMinuteTwo = temp / 60
self.iSecondsTwo = temp % 60
self.minuteTwo = "\(iMinuteTwo)"
if(self.iSecondsTwo < 10)
{
self.secondsTwo = "0\(self.iSecondsTwo)"
} else {
self.secondsTwo = "\(self.iSecondsTwo)"
}
self.timeTwoString = "\(self.iMinuteTwo).\(self.secondsTwo)"
}
}
extensions.swift
import Foundation
extension String {
func toDouble() -> Double {
if let unwrappedNum = Double(self) {
return unwrappedNum
} else {
// Handle a bad number
print("Error converting \"" + self + "\" to Double")
return 0.0
}
}
}
extension Double {
func toDecimalPlace(f: String) -> String {
return NSString(format: "%0.\(f)f", self) as String
}
func decimalValue() -> Int
{
if let decimalString = String(self).componentsSeparatedByString(".").last, decimal = Int(decimalString) {
return decimal // 535
} else {
return -1
}
}
}
functions.swift
import Foundation
func readFile(fileName: String) -> String{
if let filepath = NSBundle.mainBundle().pathForResource(fileName, ofType: "txt") {
do {
let contents = try NSString(contentsOfFile: filepath, usedEncoding: nil) as String
print(contents)
return contents
} catch {
NSLog("Content Could Not Be Loaded")
return ""
}
} else {
NSLog("File \(fileName).\("txt") could not be found")
return ""
}
}
func runAppleScript(script:String) -> String
{
let theResult:String
let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()
let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
if let _ = theDiscriptor.stringValue
{
theResult = theDiscriptor.stringValue!
} else {
theResult = ""
}
return theResult
}
errorClases.swift
import Foundation
enum MyAppleScriptError: ErrorType {
case ExecutingScriptFailed
case GettingStringValueFailed
}
Last but not least durationOfSong.txt (AppleScript)
tell application "Spotify"
set songLength to (duration of current track) / 1000 as string
return songLength
end tell
And playerPosition
tell application "Spotify"
set currentPosistion to player position as string
return currentPosistion
end tell
Edit:
Updated code
I recommend you learn how to call AppleScript handlers directly from Swift via the AppleScript-ObjC bridge. That will work vastly better than mucking about with code strings and NSAppleScript, which is slow and nasty and not a good way to do it. Tutorial here:
http://macscripter.net/viewtopic.php?id=43127
--
ETA:
"I have successful moved NSAppleScript to Swift2. Only thing is that still doesn't fix the issue. it is still out of timing."
Well, that's annoying. FWIW, I'd ditch that twoDiffrntTimes class as it doesn't do anything that can't be done in a single-line function, and change the end of main() to this:
func main() {
...
lblCurrentProgress.stringValue = formatTime(Int(playerPosistion))
lblTrackLength.stringValue = formatTime(Int(trackLength-playerPosistion))
}
func formatTime(secs: Int) -> String {
return NSString(format: "%i.%02i", secs / 60, secs % 60) as String
}

Resources