API on collection not available in protocol extension - arrays

There is some documented API in Swift for moving items in an array.
The following extension does not compile.
Value of type [String] has no member move.
protocol Database: AnyObject {
var records: [String] { get set }
}
extension Database {
func move(from source: IndexSet, to destination: Int) throws {
records.move(fromOffsets: source, toOffset: destination)
// try save()
}
}
Any help appreciated.

The documentation states that this API is part of the standard library, yet it isn't available if you import Swift (or Foundation or UIKit).
This API is available in SwiftUI!

Related

Gametracker Scraper discord.js

So I'm trying to script something that shows how many players are on a Server using gametracker. I found this module. I successfully can log those information in my terminal but i want to display. So when somebody types .info that this information are in the channel.
My Code:
case 'info':
const module = require ('gametracker-scraper')
async function output() {
let info = await module.get('My Link')
message.channel.send('Our Server Info:' + info)
}
output()
The text that is posted in the Channel when you write .info:
Our Server Info: [object Object]
Other scraper alternatives are welcome!
It is outputting "[object Object]" because you're concatenating a string and an object.
If you want to output the actual info, you could loop over the object (with, as an example, a for in loop), like this example:
let infoString = 'Our Server Info:\n';
for (const element in info) {
infoString += `${element}: ${info[element]}\n`;
}
message.channel.send(infoString);
Which would produce something like this:
Our Server Info:
Map: (your map)
Online: (your player count)
// etc...
info is an Object. Trying to concatenate an object with a string will return "[object Object]", unless it has a custom toString() method. You can access primitives from that object (using variable.property or variable["property"]) and it should concatenate properly. Another way, not as readable, is using JSON.stringify(). This will turn the object into a string
message.channel.send('Our Server Info:\n' + JSON.stringify(info))

user: getMemberGroups method is not present in Graph java sdk 1.6

I am using graph API JAVA sdk(v1.6.0) and I am trying to get all the groups that a specific user is in.
https://learn.microsoft.com/en-us/graph/api/user-getmembergroups?view=graph-rest-1.0&tabs=java#example
According to API doc, I could do something like below:
graphClient.me()
.getMemberGroups(securityEnabledOnly)
.buildRequest()
.post();
However, in java sdk 1.6.0, getMemberGroups() is not present under UserRequestBuilder.java class.
https://github.com/microsoftgraph/msgraph-sdk-java/blob/0041e58287f02036c37a8ae0a1bf30f1f616991a/src/main/java/com/microsoft/graph/requests/extensions/UserRequestBuilder.java.
Am i missing something?
I think they have replaced the getMemberOf() method with another one memberOf().
I was able to fetch all the groups for a particular user using the below code:
public static void getUserGroups(String accessToken) {
ensureGraphClient(accessToken);
String upn="test#testDomain.com";
IDirectoryObjectCollectionWithReferencesPage iDirectoryObjectCollectionWithReferencesPage = graphClient.users(upn).memberOf().buildRequest().get();
List<DirectoryObject> directoryObjects = iDirectoryObjectCollectionWithReferencesPage.getCurrentPage();
for (DirectoryObject directoryObject : directoryObjects) {
if(directoryObject.oDataType.equalsIgnoreCase("#microsoft.graph.group")) {
System.out.println(directoryObject.getRawObject().get("displayName"));
}
}
}

CKModifyBadgeOperation is deprecated in iOS 11. Anyone know an alternative approach?

I have searched, and I cannot find an example. I have also tried adapting this code (recommended elsewhere (CloudKit won't reset my badge count to 0):
func resetBadgeCounter() {
let badgeResetOperation = CKModifyBadgeOperation(badgeValue: 0)
badgeResetOperation.modifyBadgeCompletionBlock = { (error) -> Void in
if error != nil {
print("Error resetting badge: \(String(describing: error))")
}
else {
UIApplication.shared.applicationIconBadgeNumber = 0
}
}
CKContainer.default().add(badgeResetOperation)
}
This works for now, but is no longer supported, and may go away soon.
I thought perhaps I should use a CKModfyRecordsOperation or some other CKDatabaseOperation, but I can't even guess how.
I hope this helps someone as there doesn't seem to be another solution/workaround to this.
Create a Notification Service Extension (UNNotificationServiceExtension) for your app. It is fairly straightforward and is described in detail in Modifying Content in Newly Delivered Notifications. You might already have one, if you do any kind of processing on incoming notifications.
Within the extension, you typically do all the processing of the notification in override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void). The request comes with the content of the notification, which includes the badge number. You can then modify that and pass on to the contentHandler to be displayed. (If you are unsure how to do this, check the link above; it has detailed instructions and code.)
To keep track and reset the badge number when needed, you need to take advantage of an app group (e.g. group.com.tzatziki). This way you can share data between the app and the extension. Creating an app group in Xcode can be done by adding the relevant capability and is explained in App Extension Programming Guide: Handling Common Scenarios.
Use a storage mechanism (within the app group) to keep track of the badge count, e.g. UserDefaults, and use it in the extension to update the badge count. In the aforementioned override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void), you could write something like this:
let badgeNumber = UserDefaults(suiteName: "group.com.tzatziki")?.value(forKey: "badgeNumber") as? NSNumber ?? NSNumber(integerLiteral: 0)
let badgeNumberInt = badgeNumber.intValue + 1
notificationContent.badge = NSNumber(value: badgeNumberInt)
UserDefaults(suiteName: "group.io.prata")?.setValue(notificationContent.badge, forKey: "badgeNumber")
where notificationContent is an instance of UNMutableNotificationContent, originating from request.content.mutableCopy() as? UNMutableNotificationContent
Now, the only thing left to do is to reset the badge count in your app, in the shared UserDefaults and in UIApplication.shared.applicationIconBadgeNumber.
Cheers!
It was reported here: https://levelup.gitconnected.com/how-to-reset-badge-value-of-cloudkit-remote-notification-on-ios-ipados-15-46726a435599 that Apple is aware that they need to maintain CKModifyBadgeOperation until they come up with a replacement API. There is no other solution to this problem because CloudKit resets the badge number whenever it pushes a new notification.
It's probably best to just track the items you are counting and set the app badge count yourself. I reference a local database that has my items in it, and I return the total and set my app badge accordingly.

How to segue a JSON file in swift?

I am building an app where people need to fill out a form and then it creates an HTTP post where the API will return a Json file to the app with data I need. Everything is working fine with accessing the API however I want to parse the data in another view controller. How can I access the JSON file from another view controller?
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
^^^^^^^
How do I take this JSON file to the next view controller so I dont have to do the parsing below?
let jsonData = json
let ratesJson = jsonData["rates"]!
let rates = ratesJson as! NSArray
print("Rates: \(rates)")
print("*************")
print(rates.count)
print("*************")
for item in 0..<rates.count {
let specificRate = rates[item]
let price = (specificRate as AnyObject)["amount_local"]!
let provider = (specificRate as AnyObject)["provider"]!
print("--\(item)--")
print("Price: \(price!)")
print("Provider: \(provider!)")
}
}
} catch let error {
print(error.localizedDescription)
}
})
I assume by your comment, your intention is to actually pass a JSON object, not a JSON file to the next view controller.
Therefore, you just need to pass the JSON object to your segue and assign it as a property to the next view controller.
Since the question was very open ended, here is one possible solution and interpretation.
Example:
//Sample snippet from code from question
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
performSegue(withIdentifier: "MySegue", sender: json);
}
override func prepare(for segue: UIStoryboardSegue!, sender: Any?) {
if (segue.identifier == "MySegue") {
let vc = segue.destinationViewController as! RandomViewController
vc.json = sender as! [String:Any];
}
}
One of the things that usually cause bugs in programs is state. What you are asking here in my opinion is state.
What I would do is i would write a generic function ( Swift encourages functional programming in a lot of cases, have a look at: swift map, filter etc. that deals with the HTTP request and returning the data in a closure.
This closure will then live within the view controller you want your json object in, thus solving your problem of accessing said data.
Now I don't believe that is the right approach either. I would create an object for your specific data returned, like say a "struct Person" or whatever your needs are.
This has 2 advantages:
In swift 3.1 you can have a custom init() method that parses the JSON safely ( remember, optionals! ) and populates the structs data accordingly
In swift 4.0 and Xcode9.0 You will be able to morph the struct you just created to conform to the Codable and Decodable protocols with wich json parsing will be greatly simplified, as explained here: ultimate swift json guide
Hope this was of any help.

Firebase Storage Async Image Download not in the right order

I am met with a problem when I tried to download a batch of images off firebase storage. Basically, because the file sizes differ, the images are not appended to the image array properly causing the images to be in the wrong order that I wanted. Below is the code
import Foundation
import FirebaseStorage
class GalleryCellDetailedData {
var selectedGallery:String?
var count:Int
init(selectedGallery:String?,count:Int){
self.selectedGallery = selectedGallery
self.count = count
}
func addImages(completion:(data:[NSData])->()){
var datas = [NSData]()
let myGroup = dispatch_group_create()
for i in 0..<count {
dispatch_group_enter(myGroup)
getImage(i, completion: { (image:NSData) in
datas.append(image)
print("Finish Request \(i)")
dispatch_group_leave(myGroup)
})
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
completion(data: datas)
})
}
private func getImage(number:Int, completion:(image:NSData)->()){
let storage = FIRStorage.storage()
//Reference to Firebase Profile Picture Storage
let storageRef = storage.referenceForURL("gs://mannacatering-addcb.appspot.com")
print("Initiating Image Download")
let galleryPicRef = storageRef.child("Gallery/\(selectedGallery!)/g\(String(number)).jpg")
//Download Image
galleryPicRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("fail to download image")
}
dispatch_async(dispatch_get_main_queue(), {
print("Dispatching image")
completion(image:data!)
})
}
}
}
I split them up into 2 separate functions because I tried to manage them in a single function and the order is in a mess as well and I thought this might work but apparently not.
Instead of storing your data in an array, store it in a dictionary. The key can be the number i or however you want to refer to an image when you use it.
I recommend taking an approach similar to Storing multiple images into firebase and getting urls, where you store the URLs in the Realtime Database, and use that as your source of truth for ordering, display, etc. It's far easier and makes for better apps :)

Resources