I'm facing a weird behavior in my IOs application, let me comment you a bit about it:
General method called from the UI
func GetSensorList(){
dispatch_async(dispatch_get_global_queue(priority, 0)) {
self.sensors = Sensor.GenerateSensorList()
dispatch_async(dispatch_get_main_queue()) {
self.collectionView?.reloadData()
}
}
}
I have this method that call a rest Web Service:
static func GenerateSensorList() -> [Sensor]{
var sensores = [Sensor]()
let manager: AppManager = AppManager.manager
var userData: UserData? = nil
do{
userData = try manager.GetUserData()
if let userDataAux = userData {
manager.SaveSharedUserData(userDataAux)
if(userDataAux.weatherSettings!.weatherCity != nil){
var unit = "imperial"
if(userDataAux.weatherSettings!.tempFormat! == "C"){
unit = "metric"
}
let s = userDataAux.weatherSettings!.weatherCity!.stringByReplacingOccurrencesOfString(" ", withString: "#")
let weather = try manager.GetWeatherData(s, metric: unit)
let temperatureInt = Int(weather.weatherMain!.temp!)
let description = weather.weatherItem![0].description
let temp = "Temp: " + String(temperatureInt) + "°" + (userData!.weatherSettings!.tempFormat!)
let sensorAux = Sensor(image: weather.image, label1: description , label2:temp)
sensores.append(sensorAux)
}
}
let deviceData = try? manager.RetrieveDeviceDataObject()
if(deviceData != nil){
if(deviceData!?.DeviceDataItems != nil){
let deviceDataItems = deviceData!?.DeviceDataItems!
for(var i = 0; i < deviceDataItems?.count; i++){
let catId = deviceDataItems![i].CategoryId
let devId = deviceDataItems![i].DeviceItemId
switch catId!{
case 12: break
case 16:
let devItem = try manager.GetDeviceHumiditySensorItemById(devId!)
let photo1 = UIImage(named: "ic_devices")!
var humidityValue = ""
if(devItem.DeviceItemHumiditySensorHumidity != nil){
humidityValue = (devItem.DeviceItemHumiditySensorHumidity)!
}else{
humidityValue = "0"
}
let sensorAux = Sensor(image: photo1, label1: devItem.DeviceItemName , label2: ( humidityValue + "%"))
sensores.append(sensorAux)
case 17:
let devItem = try manager.GetDeviceTemperatureSensorItemById(devId!)
let photo1 = UIImage(named: "ic_devices")!
let sensorAux = Sensor(image: photo1, label1: devItem.DeviceItemName , label2: ((devItem.DeviceItemTemperatureSensorTemperature)! + "°" + (userData!.weatherSettings!.tempFormat!)))
sensores.append(sensorAux)
case 18:
let devItem = try manager.GetDeviceLightSensorItemById(devId!)
let photo1 = UIImage(named: "ic_devices")!
let sensorAux = Sensor(image: photo1, label1: devItem.DeviceItemName , label2: ((devItem.DeviceItemLightSensorLight)!))
sensores.append(sensorAux)
case 21:
let devItem = try manager.GetDevicePowerMeterItemById(devId!)
let photo1 = UIImage(named: "ic_devices")!
let sensorAux = Sensor(image: photo1, label1: devItem.DeviceItemName , label2: ((devItem.DeviceItemPowerMeterWatts)! + "Watts"))
sensores.append(sensorAux)
default: break
}
}
}
}
}
catch{
sensores = [Sensor]()
}
return sensores
}
In the line:
let weather = try manager.GetWeatherData(s, metric: unit)
I face the following issue: When I'm using the iPad emulator the method works fine, but when I'm using an iPhone 6 emulator I found that the data is different and the application crash.
I checked and the iPhone and the iPad is running the same version of IOs (9.2), the url is exactly the same, but the NSDATA object that I got are different.
GetWeatherData code:
func GetWeatherData(cityName: String, metric: String) throws -> WeatherCondition{
do{
var weather = WeatherCondition()
let url = SERVICEURL + "/GetWeather/" + cityName + "/" + metric
let data = try ExecuteRequestServiceHeader(url, mmsAuth: nil, mmsAuthSig: nil, mmsSession: nil)
if let dataAux = data{
let json = try NSJSONSerialization.JSONObjectWithData(dataAux, options: .MutableLeaves) as! NSDictionary
let jsonleave = json["GetWeatherResult"] as? String
if let jsonLeaveAux = jsonleave{
weather = WeatherCondition.JsonToObject(jsonLeaveAux)
let iconVar = weather.weatherItem![0].icon
let urlIcon = SERVICEURL + "/GetWeatherIcon/"+iconVar!
let dataIcon = try ExecuteRequestService(urlIcon)
if let dataIconAux = dataIcon{
let jsonIcon = try NSJSONSerialization.JSONObjectWithData(dataIconAux, options: .MutableLeaves) as! NSDictionary
if let jsonleaveIcon = jsonIcon["GetWeatherIconResult"] as? NSArray{
var byteArray = [UInt8]()
for (var i = 0; i < jsonleaveIcon.count; i++){
byteArray.append(UInt8(String(jsonleaveIcon[i]))!)
}
let imData = NSData(bytes: byteArray, length: (byteArray.count))
let image = UIImage(data: imData)
weather.image = image
}
}else{
throw AppManagerError.ErrorAccessingService(url: "Getting Weather data")
}
}else{
throw AppManagerError.ErrorAccessingService(url: "Getting Weather data")
}
}else{
throw AppManagerError.ErrorAccessingService(url: "Getting Weather data")
}
return weather
}catch{
throw AppManagerError.ErrorAccessingService(url: "Getting Weather data")
}
}
The application throw an exception in this line:
let json = try NSJSONSerialization.JSONObjectWithData(dataAux, options: .MutableLeaves) as! NSDictionary
Exception Track:
caught: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not
start with array or object and option to allow fragments not set."
UserInfo={NSDebugDescription=JSON text did not start with array or
object and option to allow fragments not set.}
I will appreciate any help on this
This was getting to complicated to write in a comment, so I will put this in answers.
First off, I am a bit confused by this part of your code:
do{
var weather = WeatherCondition()
let url = SERVICEURL + "/GetWeather/" + cityName + "/" + metric
let data = try ExecuteRequestServiceHeader(url, mmsAuth: nil, mmsAuthSig: nil, mmsSession: nil)
if let dataAux = data{
let json = try NSJSONSerialization.JSONObjectWithData(dataAux, options: .MutableLeaves) as! NSDictionary
You look like are you trying to create the dataAux variable based on trying to serialize dataAux not data. I would assume you want to call the JSONObjectWithData method on data. Not sure why you would see a difference on the different platforms, but that could be part of the problem.
In addition, it is worth calling the JSONObjectWithData method using .AllowFragments to see if this helps. I would also suggest taking doing something like this to see what the data actually looks like before you try to serialize it. At the very least it might help you trouble shoot the difference between the two platforms.
print(NSString(data: data, encoding: NSUTF8StringEncoding))
Related
So I have managed to play a video on Chromecast. But only one at a time. I've been trying to figure how to programmatically add to the queue. The idea is to keep playing videos all day. In the code below "playthisvideo()" randomly returns a string that contain an http://.....mp4 . I've look at Google's documentation, it's either too vague or I just don't understand it. And I can't seem to find any examples that would lead the way for me to follow.
func castthevideo() {
let metadata = GCKMediaMetadata()
metadata.setString("Los Simpsons", forKey: kGCKMetadataKeyTitle)
metadata.setString ("Barista: ¿Cómo tomas tu café? " +
" Yo: Muy, muy en serio.",
forKey: kGCKMetadataKeySubtitle)
metadata.addImage(GCKImage(url: URL(string: "https://m.media-amazon.com/images/M/MV5BYjFkMTlkYWUtZWFhNy00M2FmLThiOTYtYTRiYjVlZWYxNmJkXkEyXkFqcGdeQXVyNTAyODkwOQ##._V1_.jpg")!,
width: 480,
height: 360))
let PTV = playthisvideo()
let url = URL.init(string: PTV)
print ("****** ", PTV)
guard let mediaURL = url else {
print("****** invalid mediaURL")
return }
//let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
let mediaInformation = mediaInfoBuilder.build()
if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) { request.delegate = self }
GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}
func castanthor(byAppending appending: Bool) {
let PTV = playthisvideo()
let url = URL.init(string: PTV)
guard let mediaURL = url else {
print("invalid mediaURL")
return
}
myNSNumber = (1 as NSNumber)
if let remoteMediaClient = GCKCastContext.sharedInstance().sessionManager.currentCastSession?.remoteMediaClient {
let builder = GCKMediaQueueItemBuilder()
builder.mediaInformation = selectedItem.mediaInfo
builder.autoplay = true
builder.preloadTime = 3
let item = builder.build
if remoteMediaClient.mediaStatus != nil, appending {
let request = remoteMediaClient.queueInsert(item(), beforeItemWithID: kGCKMediaQueueInvalidItemID)
request.delegate = self
} else {
let options = GCKMediaQueueLoadOptions()
options.repeatMode = remoteMediaClient.mediaStatus?.queueRepeatMode ?? .off
let request = castSession.remoteMediaClient?.queueLoad([item()], with: options)
request?.delegate = self
}
}}
var mediaItems = [GCKMediaQueueItem]()
var urls = // Array of only audio and videos
for index in 0..<urls.count {
let builder = GCKMediaQueueItemBuilder()
let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: urls[i])
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
let mediaInformation = mediaInfoBuilder.build()
builder.mediaInformation = mediaInformation
builder.autoplay = true
builder.preloadTime = 3
let item = builder.build
mediaItems.append(item)
}
if let remoteMediaClient = GCKCastContext.sharedInstance().sessionManager.currentCastSession?.remoteMediaClient {
let loadOptions = GCKMediaQueueLoadOptions()
loadOptions.repeatMode = .all
loadOptions.startPosition = 0
remoteMediaClient.queueLoadItems(mediaItems, withOptions:loadOptions)
}
In my App I want to make it possible, that the user sets an StarRating from 0 to 5 for any Image he has in his PhotoLibrary. My research shows, that there are a couple of ways to get this done:
Save the exif metadata using the new PHPhotoLibrary
Swift: Custom camera save modified metadata with image
Writing a Photo with Metadata using Photokit
Most of these Answers were creating a new Photo. My snippet now looks like this:
let options = PHContentEditingInputRequestOptions()
options.isNetworkAccessAllowed = true
self.requestContentEditingInput(with: options, completionHandler: {
(contentEditingInput, _) -> Void in
if contentEditingInput != nil {
if let url = contentEditingInput!.fullSizeImageURL {
if let nsurl = url as? NSURL {
if let imageSource = CGImageSourceCreateWithURL(nsurl, nil) {
var imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary?
if imageProperties != nil {
imageProperties![kCGImagePropertyIPTCStarRating] = rating as AnyObject
let imageData = NSMutableData(contentsOf: url)
let image = UIImage(contentsOfFile: url.path)
let destination = CGImageDestinationCreateWithData(imageData!, CGImageSourceGetType(imageSource)!, 1, nil)
CGImageDestinationAddImage(destination!, image!.cgImage!, imageProperties! as CFDictionary)
var contentEditingOutput : PHContentEditingOutput? = nil
if CGImageDestinationFinalize(destination!) {
let archievedData = NSKeyedArchiver.archivedData(withRootObject: rating)
let identifier = "com.example.starrating"
let adjustmentData = PHAdjustmentData(formatIdentifier: identifier, formatVersion: "1.0", data: archievedData)
contentEditingOutput = PHContentEditingOutput(contentEditingInput: contentEditingInput!)
contentEditingOutput!.adjustmentData = adjustmentData
if imageData!.write(to: contentEditingOutput!.renderedContentURL, atomically: true) {
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest(for: self)
request.contentEditingOutput = contentEditingOutput
}, completionHandler: {
success, error in
if success && error == nil {
completion(true)
} else {
completion(false)
}
})
}
} else {
completion(false)
}
}
}
}
}
}
})
Now when I want to read the metadata from the PHAsset I request the ContentEditingInput again and do the following:
if let url = contentEditingInput!.fullSizeImageURL {
if let nsurl = url as? NSURL {
if let imageSource = CGImageSourceCreateWithURL(nsurl, nil) {
if let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary? {
if let starRating = imageProperties[kCGImagePropertyIPTCStarRating] as? Int {
rating = starRating
}
}
}
}
}
But I never get my rating because it says that the value of imageProperties[kCGImagePropertyIPTCStarRating] is nil.
I also tried the examples from the Answers I posted above, but I always get the same result.
I hope anybody knows, what I can do to change the Metadata.
Also, how can I change the Metadata from an PHAsset with the MediaType .video? I tried to achieve that through the AVAssetWriter and AVExportSession Objects, but in both cases it does not work. Here what I tried for Videos:
var exportSession = AVAssetExportSession(asset: asset!, presetName: AVAssetExportPresetPassthrough)
exportSession!.outputURL = outputURL
exportSession!.outputFileType = AVFileTypeQuickTimeMovie
exportSession!.timeRange = CMTimeRange(start: start, duration: duration)
var modifiedMetadata = asset!.metadata
let metadataItem = AVMutableMetadataItem()
metadataItem.keySpace = AVMetadataKeySpaceQuickTimeMetadata
metadataItem.key = AVMetadataQuickTimeMetadataKeyRatingUser as NSCopying & NSObjectProtocol
metadataItem.value = rating as NSCopying & NSObjectProtocol
modifiedMetadata.append(metadataItem)
exportSession!.metadata = modifiedMetadata
exportSession!.exportAsynchronously(completionHandler: {
let status = exportSession?.status
let success = status == AVAssetExportSessionStatus.completed
if success {
do {
let sourceURL = urlAsset.url
let manager = FileManager.default
_ = try manager.removeItem(at: sourceURL)
_ = try manager.moveItem(at: outputURL, to: sourceURL)
} catch {
LogError("\(error)")
completion(false)
}
} else {
LogError("\(exportSession!.error!)")
completion(false)
}
})
Sorry this isn't a full answer but it covers one part of your question. I noticed you are placing the StarRating in the wrong place. You need to place it in a IPTC dictionary. Also the properties data is stored as strings. Given you have the imageProperties you can add the star rating as follows and read it back using the following two functions
func setIPTCStarRating(imageProperties : NSMutableDictionary, rating : Int) {
if let iptc = imageProperties[kCGImagePropertyIPTCDictionary] as? NSMutableDictionary {
iptc[kCGImagePropertyIPTCStarRating] = String(rating)
} else {
let iptc = NSMutableDictionary()
iptc[kCGImagePropertyIPTCStarRating] = String(rating)
imageProperties[kCGImagePropertyIPTCDictionary] = iptc
}
}
func getIPTCStarRating(imageProperties : NSMutableDictionary) -> Int? {
if let iptc = imageProperties[kCGImagePropertyIPTCDictionary] as? NSDictionary {
if let starRating = iptc[kCGImagePropertyIPTCStarRating] as? String {
return Int(starRating)
}
}
return nil
}
As the imageProperties you get from the image are not mutable you need to create a mutable copy of these properties first before you can call the functions above. When you create your image to save use the mutable properties in your call to CGImageDestinationAddImage()
if let mutableProperties = imageProperties.mutableCopy() as? NSMutableDictionary {
setIPTCStarRating(imageProperties:mutableProperties, rating:rating)
}
One other point you are creating an unnecessary UIImage. If you use CGImageDestinationAddImageFromSource() instead of CGImageDestinationAddImage() you can use the imageSource you created earlier instead of loading the image data into a UIImage.
I'm just wondering if there is any way to boost speed of my loop, or suggestions for best practice, cause I feel it looks so bad.
Here is the code:
for (index, _) in filteredArray.enumerate() {
if index == 0 || index % 4 == 0 {
let mediaItem = Item()
mediaItem.id = filteredArray[index + 3]
let photoURL = NSURL(string: filteredArray[index + 1])
guard let url = photoURL else { return }
let data = NSData(contentsOfURL: url)
let finishImage = UIImage(data: data!)
mediaItem.Photo = finishImage
mediaItem.orderCount = filteredArray[index + 2]
mediaItem.UUId = filteredArray[index]
self.dataSourceItems.insert(mediaItem)
}
}
Try to use dispatch_apply. Something like that:
let iterationsCount = filteredArray.count / 4
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_apply(iterationsCount, queue) { i in
let index = i * 4
let mediaItem = Item()
mediaItem.id = filteredArray[index + 3]
let photoURL = NSURL(string: filteredArray[index + 1])
guard let url = photoURL else { return }
let data = NSData(contentsOfURL: url)
let finishImage = UIImage(data: data!)
mediaItem.Photo = finishImage
mediaItem.orderCount = filteredArray[index + 2]
mediaItem.UUId = filteredArray[index]
self.dataSourceItems.insert(mediaItem)
}
Notice that, depending on your situation, you may need to 1. use self inside closure, if you accessing properties; 2. add some locks if you write to shared memory.
I'm new to swift programming.
I wrote a code which gets xml output from rss feed and parse it into NSDATA type,
then I want to get title and image src values from it in a loop and send them to a table view to create a list.
when I get those values manually I mean like :
let appName = xml["rss"]["channel"]["item"][0]["title"].element!.text!
let appUrl = xml["rss"]["channel"]["item"][0]["description"]["img"].element!.attributes["src"]
my code works ok and one item creates in table view correctly.
but the problem is when I want to get all values from xml file.
I couldn't create and array of the xml which is NSDATA type, not a string to be able to loop through it.
all I could manage to work is the below code which returns all xml tags value which I don't want that :
func enumerate(indexer: XMLIndexer, level: Int) {
for child in indexer.children {
let appName = child.element!.text
let appUrl = child.element!.attributes["src"]
let ap = Apps(name: appName , img : appUrl)
self.tableData.append(ap)
self.tableView.reloadData()
enumerate(child, level: level + 1)
}
}
enumerate(xml, level: 0)
Any Idea how to get those values in a loop without mistaking or getting other values?
here is my code :
let url = NSURL(string: "http://razavitv.aqr.ir/index/rss/2")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
let processedString = (dataString as NSString).stringByReplacingOccurrencesOfString("<![CDATA[", withString: "").stringByReplacingOccurrencesOfString("]]", withString: "") as String
let data: NSData = processedString.dataUsingEncoding(NSUTF8StringEncoding)!
let xml = SWXMLHash.parse(data)
// let appName = xml["rss"]["channel"]["item"][0]["title"].element!.text!
// let appUrl = xml["rss"]["channel"]["item"][0]["description"]["img"].element!.attributes["src"]
//
// let ap = Apps(name: appName , img : appUrl)
// self.tableData.append(ap)
//
// self.tableView.reloadData()
func enumerate(indexer: XMLIndexer, level: Int) {
for child in indexer.children {
let appName = child.element!.text
let appUrl = child.element!.attributes["src"]
let ap = Apps(name: appName , img : appUrl)
self.tableData.append(ap)
self.tableView.reloadData()
enumerate(child, level: level + 1)
}
}
enumerate(xml, level: 0)
}
task.resume()
very simple solution : just need to create a for loop with indexer inside.
here is the code :
//one root element
let count = xml["rss"]["channel"]["item"].all.count
for var i = 0; i < count; i++ {
let appName = xml["rss"]["channel"]["item"][i]["title"].element!.text!
let appUrl = xml["rss"]["channel"]["item"][i]["description"]["img"].element!.attributes["src"]
let ap = Apps(name: appName , img : appUrl)
self.tableData.append(ap)
self.tableView.reloadData()
}
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)
}