For some reason, Microsoft AdaptiveCards dropdown is not working on iOS.
What I'm I missing?
Here's the Json:
Json
Here's the code I'm using to render the card:
let url = Bundle.main.url(forResource: "adaptiveCard", withExtension: "json")!
let data = try! Data(contentsOf: url)
let jsonStr = String(data: data, encoding: String.Encoding.utf8)
let cardParseResult = ACOAdaptiveCard.fromJson(jsonStr);
let config = ACOHostConfig()
if((cardParseResult?.isValid)!) {
let renderResult = ACRRenderer.render(cardParseResult!.card, config: config, widthConstraint: Float(bounds.width))
if(renderResult?.succeeded ?? false) {
if let view = renderResult?.view {
context.coordinator.setDelegate(view: view)
return view
}
}
}
Here's a screenshot of the screen:
Related
I want a dictionary out of my plist, which i can use in the whole gamescene. However in my solution i always have to call the parseConfig function in order to get a dictionary from the plist.
struct Config: Decodable {
private enum CodingKeys: String, CodingKey {
case zPositions, enemy, player
}
let zPositions: [String:Double]
let enemy: [String:[String:Double]]
let player: [String:[String:Double]]
}
func parseConfig() -> Config {
let url = Bundle.main.url(forResource: "Config", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let decoder = PropertyListDecoder()
return try! decoder.decode(Config.self, from: data)
}
try crate lazy property.
Something like: - if use gloabaly
var parsedConfig: Config = {
let url = Bundle.main.url(forResource: "Config", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let decoder = PropertyListDecoder()
return try! decoder.decode(Config.self, from: data)
}()
if in class add lazy before var
You can try to write a closure to load only once
lazy var myConfig : Config = {
let url = Bundle.main.url(forResource: "Config", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let decoder = PropertyListDecoder()
return try! decoder.decode(Config.self, from: data)
}()
or inside a singleton in all the app
class Service {
static let shared = Service()
lazy var myConfig : Config = {
let url = Bundle.main.url(forResource: "Config", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let decoder = PropertyListDecoder()
return try! decoder.decode(Config.self, from: data)
}()
}
I am trying to create my own content blocker on iOS. I was wanting to create separate json lists for different types of content (tracking, ads, adult sites, etc). I came across this https://github.com/calebhicks/ios-safari-content-blocking which stated you could create an array of "attachments" instead of relying on the singular "blockerList" json file.
func beginRequest(with context: NSExtensionContext) {
var jsonFiles:Array<NSItemProvider> = Array()
let attachment = NSItemProvider(contentsOf: Bundle.main.url(forResource: "blockerList", withExtension: "json"))!
jsonFiles.append(attachment)
let attachment2 = NSItemProvider(contentsOf: Bundle.main.url(forResource: "testList", withExtension: "json"))!
jsonFiles.append(attachment2)
let item = NSExtensionItem()
item.attachments = jsonFiles
context.completeRequest(returningItems: [item], completionHandler: nil)
}
Most of this code is the default from the Content Blocker Extension setup, but what I have added is the jsonFiles array which attachment and attachment2 are placed in. When this is run, only one of the two rule sets is loaded, never a combination of the two. Any ideas on why only one ruleset is loaded?
You can combine two JSON rule files in to one file and use that file.
import UIKit
import MobileCoreServices
class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "you app group identifier")
let sourceURLRules = sharedContainerURL?.appendingPathComponent("Rules1.json")
let sourceURLRules2 = sharedContainerURL?.appendingPathComponent("Rules2.json")
do {
let jsonDecoder = JSONDecoder()
let dataFormRules1 = try Data(contentsOf: sourceURLRules1!, options: .mappedIfSafe)// Rule is Decode able Swift class
let rulesArray1 = try? jsonDecoder.decode(Array<Rule>.self,from: dataFormRules1)
let dataFormRules2 = try Data(contentsOf: sourceURLRules2!, options: .mappedIfSafe)
let rulesArray2 = try? jsonDecoder.decode(Array<Rule>.self,from: dataFormRules2)
saveCombinedRuleFile(ruleList: rulesArray1! + rulesArray2!)
} catch {
//handle error condition
}
let sourceURLCombinedRule = sharedContainerURL?.appendingPathComponent("CombinedRule.json")
let combinedRuleAttachment = NSItemProvider(contentsOf: sourceURLCombinedRule)
let item = NSExtensionItem()
item.attachments = [combinedRuleAttachment]
context.completeRequest(returningItems: [item], completionHandler: nil)
}
func saveCombinedRuleFile(ruleList:[Rule]) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(ruleList) {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "you app group identifier")
if let json = String(data: encoded, encoding: .utf8) {
print(json)
}
if let destinationURL = sharedContainerURL?.appendingPathComponent("CombinedRule.json") {
do {
try encoded.write(to: destinationURL)
} catch {
print ("catchtry")
}
}
}
}
}
I try to parse json with SwiftyJSON. One of the fields have url to image and i try to save it as NSData but I face crash and console errors. Crash appears when compiler comes to object creation
code it the following
var JSONStorage : [Article?]?
var objects = [Article?]()
override func viewDidLoad() {
super.viewDidLoad()
let number = arc4random_uniform(1000)
let urlString = "http://wirehead.ru/article.json?\(number)"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = JSON(data: data)
for element in json["article"].arrayValue {
let id = Int(element["id"].stringValue)
let title = element["title"].stringValue
let subtitle = element["subtitle"].stringValue
let body = element["body"].stringValue
let img = element["images"]["main"].rawValue
let obj:Article = Article(id: id!, title: title, subtitle: subtitle, body: body, mainImage: img as! NSData)
objects.append(obj)
print("We are inside if let")
}
}
}
print(objects)
}
Link to JSON is http://wirehead.ru/article.json and here is with highlight http://pastebin.com/AAEFjsQN
Error that I get is
Any advice ?
["images"]["main"] contains an URL represented by a String
To get the image data, use something like this
let imgURLString = element["images"]["main"].stringValue
if let url = NSURL(string:imgURLString) {
let img = NSData(contentsOfURL:url)
}
Is it possible to return multiple JSON files from a Content Blocker Extension? In my UI users enable / disable different filters and each filter is represented by a separate file. I currently have (which only loads one despite iterating through multiple):
func beginRequestWithExtensionContext(context: NSExtensionContext) {
var items = Array <NSExtensionItem>()
let resources = ["a", "b", "c"]
for resource in resources {
let url = NSBundle.mainBundle().URLForResource(resource, withExtension: "json")
if let attachment = NSItemProvider(contentsOfURL: url) {
let item = NSExtensionItem()
item.attachments = [attachment]
items.append(item)
}
}
context.completeRequestReturningItems(items, completionHandler: nil)
}
I've tried doing multiple items and a single item with multiple attachments. If it isn't possible to have separate files, any way to combine multiple (or generate programmatically)?
It is possible to have multiple JSON files and use it for the Content Blocker extension.
1) Throws SFContentBlockerErrorDomain when you pass multiple extension items to completeRequestReturningItems method.
2) Can't attach multiple attachments to NSExtension. The comment on the source code says, the attachment is not meant to be an array of alternate data formats/types, but instead a collection to include in a social media post for example. These items are always typed NSItemProvider. I reckon you wouldn't be able to add multiple JSON data as attachments, since they are not a series of attachments to create a message.
My Solution (Verified it works):
NSItemProvider can be initialised with item (NSData) and typeIdentifier.
let aData = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("a", withExtension: "json")!)
let bData = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("b", withExtension: "json")!)
aJSON = `convert aData to JSON`
bJSON = `convert bData to JSON`
combinedJSON = `aJSON + bJSON`
combinedData = 'convert combinedJSON to NSData'
let attachment = NSItemProvider(item: combinedData, typeIdentifier: kUTTypeJSON as String)
Now you could create the extension with the attachment, combinedData as per your preferences.
For those curious I ended up adding code to dynamically generate a JSON file (persisted to disk). From other answers it seems like the step of saving could be avoided by returning an NSData representation of the file instead - although that attempt failed for me. Here's my snippet:
import UIKit
import MobileCoreServices
class ActionRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequestWithExtensionContext(context: NSExtensionContext) {
let item = NSExtensionItem()
let items = [item]
let url = buildJSONFileURL()
if let attachment = NSItemProvider(contentsOfURL: url) { item.attachments = [attachment] }
context.completeRequestReturningItems(items, completionHandler: nil)
}
func buildJSONFileURL() -> NSURL {
let directories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let directory = directories[0]
let path = directory.stringByAppendingFormat("/block.json")
let selector = [...] // Dynamically Generated
let dictionary = [[
"action": [ "type": "css-display-none", "selector": selector ],
"trigger": [ "url-filter": ".*" ]
]]
let data = try! NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions.PrettyPrinted)
let text = NSString(data: data, encoding: NSASCIIStringEncoding)!
try! text.writeToFile(path, atomically: true, encoding: NSASCIIStringEncoding)
return NSURL(fileURLWithPath: path)
}
}
You can combine two JSON rule files in to one file and use that file.
import UIKit
import MobileCoreServices
class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "you app group identifier")
let sourceURLRules = sharedContainerURL?.appendingPathComponent("Rules1.json")
let sourceURLRules2 = sharedContainerURL?.appendingPathComponent("Rules2.json")
do {
let jsonDecoder = JSONDecoder()
let dataFormRules1 = try Data(contentsOf: sourceURLRules1!, options: .mappedIfSafe)// Rule is Decode able Swift class
let rulesArray1 = try? jsonDecoder.decode(Array<Rule>.self,from: dataFormRules1)
let dataFormRules2 = try Data(contentsOf: sourceURLRules2!, options: .mappedIfSafe)
let rulesArray2 = try? jsonDecoder.decode(Array<Rule>.self,from: dataFormRules2)
saveCombinedRuleFile(ruleList: rulesArray1! + rulesArray2!)
} catch {
//handle error condition
}
let sourceURLCombinedRule = sharedContainerURL?.appendingPathComponent("CombinedRule.json")
let combinedRuleAttachment = NSItemProvider(contentsOf: sourceURLCombinedRule)
let item = NSExtensionItem()
item.attachments = [combinedRuleAttachment]
context.completeRequest(returningItems: [item], completionHandler: nil)
}
func saveCombinedRuleFile(ruleList:[Rule]) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(ruleList) {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "you app group identifier")
if let json = String(data: encoded, encoding: .utf8) {
print(json)
}
if let destinationURL = sharedContainerURL?.appendingPathComponent("CombinedRule.json") {
do {
try encoded.write(to: destinationURL)
} catch {
print ("catchtry")
}
}
}
}
}
Xcode 7 Playgrounds now supports loading files from the nested Resources directory.
I can get SKScene(fileNamed: "GameScene") when I have a GameScene.sks in my Resources or NSImage(named:"GameScene.png") if I have a GameScene.png in your Resources.
But how can I read a regular text file from the Playground Resources directory as well?
We can use the Bundle.main
So, if you have a test.json in your playground like
You can access it and print its content like that:
// get the file path for the file "test.json" in the playground bundle
let filePath = Bundle.main.path(forResource:"test", ofType: "json")
// get the contentData
let contentData = FileManager.default.contents(atPath: filePath!)
// get the string
let content = String(data:contentData!, encoding:String.Encoding.utf8)
// print
print("filepath: \(filePath!)")
if let c = content {
print("content: \n\(c)")
}
Will print
filepath: /var/folders/dm/zg6yp6yj7f58khhtmt8ttfq00000gn/T/com.apple.dt.Xcode.pg/applications/Json-7800-6.app/Contents/Resources/test.json
content:
{
"name":"jc",
"company": {
"name": "Netscape",
"city": "Mountain View"
}
}
Jeremy Chone's answer, updated for Swift 3, Xcode 8:
// get the file path for the file "test.json" in the playground bundle
let filePath = Bundle.main.path(forResource: "test", ofType: "json")
// get the contentData
let contentData = FileManager.default.contents(atPath: filePath!)
// get the string
let content = String(data: contentData!, encoding: .utf8)
// print
print("filepath: \(filePath!)")
if let c = content {
print("content: \n\(c)")
}
You can use String directly with a URL. Example in Swift 3:
let url = Bundle.main.url(forResource: "test", withExtension: "json")!
let text = String(contentsOf: url)
Swift 5
It is possible to get to files in your Resources folder by the bundle in a Playground.
import UIKit
Here are two ways to get the JSON data.
Path:
guard let path = Bundle.main.path(forResource:"test", ofType: "json"),
let data = FileManager.default.contents(atPath: path) else {
fatalError("Can not get json data")
}
URL:
guard let url = Bundle.main.url(forResource:"test", withExtension: "json") else {
fatalError("Can not get json file")
}
if let data = try? Data(contentsOf: url) {
// do something with data
}
Another short way (Swift 3):
let filePath = Bundle.main.path(forResource: "test", ofType: "json")
let content: String = String(contentsOfFile: filePath!, encoding: .utf8)
Added try for swift3.1:
let url = Bundle.main.url(forResource: "test", withExtension: "json")!
// let text = String(contentsOf: url)
do {
let text = try String(contentsOf: url)
print("text: \n\(text)")
}
catch _ {
// Error handling
}
// --------------------------------------------------------------------
let filePath2 = Bundle.main.path(forResource: "test", ofType: "json")
do {
let content2: String = try String(contentsOfFile: filePath2!, encoding: .utf8)
print("content2: \n\(content2)")
}
catch _ {
// Error handling
}