How work history of web browser under the hood - algorithm

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)
}
}

Related

Create KMS key policy in Go

I'm trying to create a KMS key using the AWS SDK v2 function call:
conn := kms.NewFromConfig(cfg)
input := kms.CreateKeyInput{
KeySpec: types.KeySpecEccNistP521,
KeyUsage: types.KeyUsageTypeSignVerify,
MultiRegion: aws.Bool(true),
Policy: aws.String("")
}
output, err := conn.CreateKey(ctx, &input)
The problem I'm having is that I'm not sure how to generate the policy for the key. I assume I could create JSON for an IAM policy document, but I don't find the prospect of generating that myself to be particularly inviting. Is there a package or library that I can use to generate this document?
I ended up creating my own policy structs:
// Policy describes a policy document that can be used to configure permissions in IAM
type Policy struct {
Version string `json:"Version"`
ID string `json:"Id"`
Statements []*Statement `json:"Statement"`
}
// Statement describes a set of permissions that define what resources and users should have access
// to the resources described therein
type Statement struct {
ID string `json:"Sid"`
Effect Effect `json:"Effect"`
PrincipalArns Principals `json:"Principal"`
ActionArns Actions `json:"Action"`
ResourceArns Resources `json:"Resource"`
}
// Principals describes a list of principals associated with a policy statement
type Principals []string
// MarhsalJSON converts a Principals collection to JSON
func (p Principals) MarshalJSON() ([]byte, error) {
// First, get the inner string from the list of principals
var inner string
if len(p) > 1 {
inner = marshal(p...)
} else if len(p) == 1 {
inner = strings.Quote(p[0], "\"")
} else {
return nil, fmt.Errorf("Principal must contain at least one element")
}
// Next, create the principal block and return it
return []byte(fmt.Sprintf("{\"AWS\": %s}", inner)), nil
}
// Actions describes a list of actions that may or may not be taken by principals with regard to the
// resources described in a policy statement
type Actions []Action
// MarshalJSON converts an Actions collection to JSON
func (a Actions) MarshalJSON() ([]byte, error) {
// First, get the inner string from the list of actions
var inner string
if len(a) > 1 {
inner = marshal(a...)
} else if len(a) == 1 {
inner = strings.Quote(a[0], "\"")
} else {
return nil, fmt.Errorf("Action must contain at least one element")
}
// Next, create the action block and return it
return []byte(inner), nil
}
// Resources describes a list of resources effected by the policy statement
type Resources []string
// MarshalJSON converts a Resources collection to JSON
func (r Resources) MarshalJSON() ([]byte, error) {
// First, get the inner string from the list of actions
var inner string
if len(r) > 1 {
inner = marshal(r...)
} else if len(r) == 1 {
inner = strings.Quote(r[0], "\"")
} else {
return nil, fmt.Errorf("Resource must contain at least one element")
}
// Next, create the action block and return it
return []byte(inner), nil
}
// Helper function that converts a list of items to a JSON-string
func marshal[S ~string](items ...S) string {
return "[" + strings.ModifyAndJoin(func(item string) string { return strings.Quote(item, "\"") }, ",", items...) + "]"
}
// Effect describes the effect a policy statement will have upon the resource and for the actions described
type Effect string
var (
// Allow to grant access of the resource and actions to the principals described in the policy statement
Allow = Effect("Allow")
// Deny to deny access of the resource and actions from the principals described in the policy statement
Deny = Effect("Deny")
)
// Action describes a valid operation that may be made against a particular AWS resource
type Action string
// Describes the various action types available to AWS
var (
CancelKeyDeletion = Action("kms:CancelKeyDeletion")
ConnectCustomKeyStore = Action("kms:ConnectCustomKeyStore")
CreateAlias = Action("kms:CreateAlias")
CreateCustomKeyStore = Action("kms:CreateCustomKeyStore")
CreateGrant = Action("kms:CreateGrant")
CreateKey = Action("kms:CreateKey")
Decrypt = Action("kms:Decrypt")
DeleteAlias = Action("kms:DeleteAlias")
DeleteCustomKeyStore = Action("kms:DeleteCustomKeyStore")
DeleteImportedKeyMaterial = Action("kms:DeleteImportedKeyMaterial")
DescribeCustomKeyStores = Action("kms:DescribeCustomKeyStores")
DescribeKey = Action("kms:DescribeKey")
DisableKey = Action("kms:DisableKey")
DisableKeyRotation = Action("kms:DisableKeyRotation")
DisconnectCustomKeyStore = Action("kms:DisconnectCustomKeyStore")
EnableKey = Action("kms:EnableKey")
EnableKeyRotation = Action("kms:EnableKeyRotation")
Encrypt = Action("kms:Encrypt")
GenerateDataKey = Action("kms:GenerateDataKey")
GenerateDataKeyPair = Action("kms:GenerateDataKeyPair")
GenerateDataKeyPairWithoutPlaintext = Action("kms:GenerateDataKeyPairWithoutPlaintext")
GenerateDataKeyWithoutPlaintext = Action("kms:GenerateDataKeyWithoutPlaintext")
GenerateMac = Action("kms:GenerateMac")
GenerateRandom = Action("kms:GenerateRandom")
GetKeyPolicy = Action("kms:GetKeyPolicy")
GetKeyRotationStatus = Action("kms:GetKeyRotationStatus")
GetParametersForImport = Action("kms:GetParametersForImport")
GetPublicKey = Action("kms:GetPublicKey")
ImportKeyMaterial = Action("kms:ImportKeyMaterial")
ListAliases = Action("kms:ListAliases")
ListGrants = Action("kms:ListGrants")
ListKeyPolicies = Action("kms:ListKeyPolicies")
ListKeys = Action("kms:ListKeys")
ListResourceTags = Action("kms:ListResourceTags")
ListRetirableGrants = Action("kms:ListRetirableGrants")
PutKeyPolicy = Action("kms:PutKeyPolicy")
ReEncryptFrom = Action("kms:ReEncryptFrom")
ReEncryptTo = Action("kms:ReEncryptTo")
ReplicateKey = Action("kms:ReplicateKey")
RetireGrant = Action("kms:RetireGrant")
RevokeGrant = Action("kms:RevokeGrant")
ScheduleKeyDeletion = Action("kms:ScheduleKeyDeletion")
Sign = Action("kms:Sign")
TagResource = Action("kms:TagResource")
UntagResource = Action("kms:UntagResource")
UpdateAlias = Action("kms:UpdateAlias")
UpdateCustomKeyStore = Action("kms:UpdateCustomKeyStore")
UpdateKeyDescription = Action("kms:UpdateKeyDescription")
UpdatePrimaryRegion = Action("kms:UpdatePrimaryRegion")
Verify = Action("kms:Verify")
VerifyMac = Action("kms:VerifyMac")
KmsAll = Action("kms:*")
)
I can then use this in my code like so:
conn := kms.NewFromConfig(cfg)
policy := Policy {
Version: "2012-10-17",
ID: "test-key",
Statements: []*policy.Statement{
{
ID: "test-failure",
Effect: policy.Allow,
PrincipalArns: []string{"arn:aws:kms:eu-west-2:111122223333:root"},
ActionArns: policy.Actions{policy.KmsAll},
ResourceArns: []string{"*"},
},
},
}
pData, err := json.Marshal(policy)
if err != nil {
return err
}
input := kms.CreateKeyInput{
KeySpec: types.KeySpecEccNistP521,
KeyUsage: types.KeyUsageTypeSignVerify,
MultiRegion: aws.Bool(true),
Policy: aws.String(string(pData)),
}
output, err := conn.CreateKey(ctx, &input)
I added the code for this in an open-source package that can be found here so others can use it.

Can not querying from CoreData in swift 3

I'm using this class function to search for a record by one field. The problem here is this always returns nil, and if remove the predicate will return any row from the database. I'm using Xcode v 8.0
class func visitorWithCode(code: String, inManagedObjectContext context: NSManagedObjectContext) -> Visitor? {
print(code)
var visitor: Visitor? = nil
let fetchRequest: NSFetchRequest<Visitor> = NSFetchRequest(entityName: "Visitor")
fetchRequest.predicate = NSPredicate(format: "barcodeNo == \(code)")
fetchRequest.fetchLimit = 1
let fetchedData = try! context.fetch(fetchRequest)
if (!fetchedData.isEmpty) {
print(fetchedData.first)
visitor = fetchedData.first! as Visitor
return visitor
}
else {
return visitor
}
}

How to reset values in for loop in swift 2

I'm having trouble with the following line of code:
var participants = [String]()
var dict = [String: String]()
func createDictionary() {
var hat = participants
var loopCount = 0
for (keyIndex, key) in hat.enumerate() {
var valueIndex: Int {
var index: Int
repeat {
index = Int(arc4random_uniform(UInt32(hat.count)))
loopCount++
print(loopCount)
if loopCount > participants.count + 1000 {
createDictionary()
}
} while index == keyIndex || dict.values.contains(hat[index])
return index
}
dict[key] = hat[valueIndex]
}
}
My goal is for "dict" to contain a dictionary created from "participants". This function will work most of the time, but will sometimes enter into an infinite loop. I was trying to make it so that if it loops more than 1000 times or so, that the function would repeat and try again.
I think the problem is that I can't reset the values for "keyIndex" and "key" because those are "let constants". But I'm not sure.
Is there a way to reset the loop so those values? Or have the entire function retry?
So if anyone is interested, I was able to figure it out. I'm still new to this, so I'm sure others would have been able to figure it out quicker :)
Anyway, I updated the code as follows:
var participants = [String]()
var dict = [String: String]()
func createDictionary() {
var hat = participants
var drawName = [String: String]()
var loopCount = 0
for (keyIndex, key) in hat.enumerate() {
var valueIndex: Int {
var index: Int
repeat {
loopCount++
index = Int(arc4random_uniform(UInt32(hat.count)))
} while index == keyIndex || drawName.values.contains(hat[index]) && loopCount < hat.count + 50
return index
}
drawName[key] = hat[valueIndex]
}
if loopCount > hat.count + 30 {
self.createDictionary()
} else {
dict = drawName
}
}
mathielo was right that I was most likely looking at this the wrong way. Basically I just instructed the loop to not loop too many times, then recalled the function if it had to try too many times to get a value. The above code works without freezing the program.

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
}

Dictionary does not have a member named filter - but it should, shouldn't it?

I'm trying to get code from github for auto-completion to work, but am stuck with an error on line 6 (data.filter) that Dictionary does not have a member named filter. But everything I read in the documentation suggests dictionaries should have a filter method. I've tried every possible combination of unwrapping, self, etc, but the compiler then registers these changes as the error.
Obviously something is going on that I do not understand - any guidance is appreciated.
var data = Dictionary<String, AnyObject>()
func applyFilterWithSearchQuery(filter : String) -> Dictionary<String, AnyObject>
{
var lower = (filter as NSString).lowercaseString
if (data.count > 0) {
var filteredData = data.filter ({
if let match : AnyObject = $0["DisplayText"]{
return (match as NSString).lowercaseString.hasPrefix((filter as NSString).lowercaseString)
}
else{
return false
}
})
}
return filteredData
}
Never heard of dictionary filters - where have you read about that? Arrays do have a filter method, but not dictionaries. What you can do is filter keys or values (accessible via the respective dictionary properties).
You can implement a custom filter on a dictionary with the following code:
var filtered = dict.keys.filter { $0.hasPrefix("t") }.map { (key: $0, value: dict[$0]) }
var newDict = [String : AnyObject]()
for element in filtered {
newDict[element.key] = element.value
}
which filter keys, then maps each key to a (key, value) tuple, then add each tuple to a new dictionary.
Note that to filter keys I used hasPrefix("t") - replace that with something more appropriate to your case
var data = Dictionary<String, AnyObject>()
func applyFilterWithSearchQuery(filter: String) -> [String : AnyObject] {
var lower = filter.lowercaseString
var filteredData = [String : AnyObject]()
if (!data.isEmpty) {
Swift.filter(data) {
(key, value) -> Bool in
if key == "DisplayText" {
filteredData[key] = (value as? String)?.lowercaseString.hasPrefix(lower)
}
return false
}
/* use map function works too
map(data) {
(key, value) -> Void in
if key == "DisplayText" {
filteredData[key] = (value as? String)?.lowercaseString.hasPrefix(lower)
}
}
*/
}
return filteredData
}

Resources