Couldn't parse JSON data.
Could not cast value of type '__NSArrayI' (0x10f02fc08) to 'NSMutableArray' (0x10f02fcd0).
JSON data here and code here;
echo json_encode($resultArray);
And here is my trial code;
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for i in 1...jsonResult.count
{
jsonElement = jsonResult[i-1] as! NSDictionary
let location = LocationModel()
if let name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
NOTE: IT WILL FIX THIS
instead of:
var jsonResult: NSMutableArray = NSMutableArray()
do this:
var jsonResult: NSArray = NSArray()
and instead of:
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSMutableArray
do this:
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
full code:
func parseJSON() {
var jsonResult: NSArray = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for i in 1...jsonResult.count
{
jsonElement = jsonResult[i-1] as! NSDictionary
let location = LocationModel()
if let name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
The issue is that JSONSerialization is returning a non-mutable array, and you can't just cast a NSArray to a NSMutableArray. Instead, just declare it to be NSArray:
func parseJSON() {
var jsonResult: NSArray
do {
jsonResult = try JSONSerialization.jsonObject(with: data as Data) as! NSArray
} catch let error as NSError {
print(error)
return
}
...
}
Or, alternatively, use Swift's native collection types rather than falling back on old NSArray and NSDictionary types:
func parseJSON() {
var jsonResult: [[String: Any]] // this is an array of dictionaries
do {
jsonResult = try JSONSerialization.jsonObject(with: data as Data) as! [[String: Any]]
} catch let error as NSError {
print(error)
return
}
for jsonElement in jsonResult {
if let name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
let location = LocationModel()
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
locations.add(location) // or, if you defined `locations` to be a Swift array, `locations.append(location)`
}
}
...
}
(Personally, I'd probably also define the data to be a Data rather than a NSData and define locations to be [LocationModel] rather than NSMutableArray.)
Related
I am trying to convert some Objective C code to Swift and can't manage to do it right with subscripting.
This is the method I am trying to migrate to Swift:
- (NSArray *)rangesOfSubstringAlphaNumeric:(NSString *)substring rangesLimit:(NSUInteger)rangesLimit {
NSAssert(rangesLimit, #"A range limit grather than 0 must be specified");
if (!substring.length) {
return nil;
}
static NSCharacterSet * restrictedCharacters = nil;
if (!restrictedCharacters) {
restrictedCharacters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
}
NSArray * substrings = [substring componentsSeparatedByCharactersInSet:restrictedCharacters];
NSMutableArray * allRanges = [NSMutableArray array];
NSString *searchedString = self;
for (NSString *stringToMatch in substrings) {
if (![stringToMatch isEqualToString:#""]) {
NSRange aRange;
NSUInteger lastLocation = 0;
NSUInteger foundRanges = 0;
while (foundRanges++ < rangesLimit &&
(aRange = [searchedString localizedStandardRangeOfString:stringToMatch]).location != NSNotFound) {
searchedString = [searchedString substringFromIndex:aRange.location + aRange.length];
aRange.location = aRange.location + lastLocation;
lastLocation = aRange.location + aRange.length;
[allRanges addObject:[NSValue valueWithRange:aRange]];
}
}
}
return allRanges.count ? [allRanges copy] : nil;
}
I got stuck on the subscripting part since it seems I cannot assign integer values to Indexes and conversion from Index to Int is out of hand for me I'm kind of stuck, this is what I managed to do:
func rangesOfAlphanumeric(substring: String, limit: UInt) -> [Range<String.Index>] {
guard limit > 0, !substring.isEmpty else {
if limit == 0 {
assert(false, "limit must be greather than 0")
}
return []
}
var searchedString = self
let substrings = substring.components(separatedBy: NSCharacterSet.restricted)
for stringToMatch in substrings {
if !stringToMatch.isEmpty {
// var aRange: Range<String.Index>?
// var lastLocation: UInt = 0
// var foundRanges: UInt = 0
// while foundRanges < limit,
// let tempRange = searchedString.localizedStandardRange(of: stringToMatch),
// !tempRange.isEmpty {
//
// searchedString = String(searchedString[tempRange.upperBound...])
// if let lastLocation = lastLocation {
// aRange = temp
// }
// }
}
}
}
UPDATE: Solution below.
Managed to resolve the issue using the ranges function posted here:
func rangesOfAlphanumeric(substring: String) -> [Range<String.Index>] {
var searchedString = self
let substrings = substring.components(separatedBy: NSCharacterSet.restricted)
return substrings.compactMap { (stringToMatch) -> [Range<String.Index>]? in
guard !stringToMatch.isEmpty else {
return nil
}
let ranges = searchedString.ranges(of: stringToMatch, options: [
.diacriticInsensitive,
.caseInsensitive
])
if let lastRange = ranges.last {
searchedString = String(searchedString[index(after: lastRange.upperBound)])
}
return ranges
}.flatMap{$0}
}
I created this repo with swift 5 is very easy to use
all is already set up. you have just to change the IAP ids
The Github repo
I have a simple test function which will tap a button an measure the performance. I'm using XCTest. After measureBlock returns I can see a bunch of perf-metrics on the console. I would like to get this within the test-program such that I can populate the data somewhere else programmatically. Watching the test data on test-console is proving to be slow because I have a lot of test-cases.
- (void)testUseMeasureBlock {
XCUIElement *launchTest1Button = [[XCUIApplication alloc] init].buttons[#"Launch Test 1"];
void (^blockToMeasure)(void) = ^void(void) {
[launchTest1Button tap];
};
// Run once to warm up any potential caching properties
#autoreleasepool {
blockToMeasure();
}
// Now measure the block
[self measureBlock:blockToMeasure];
/// Collect the measured metrics and send somewhere.
When we run a test it prints:
measured [Time, seconds] average: 0.594, relative standard deviation: 0.517%, values: [0.602709, 0.593631, 0.593004, 0.592350, 0.596199, 0.593807, 0.591444, 0.593460, 0.592648, 0.592769],
If I could get the average time, that'd be sufficient for now.
Since there is no API to get this data you can pipe stderr stream and parse tests logs to get needed info e.g. average time. For instance you can use next approach:
#interface MeasureParser : NSObject
#property (nonatomic) NSPipe* pipe;
#property (nonatomic) NSRegularExpression* regex;
#property (nonatomic) NSMutableDictionary* results;
#end
#implementation MeasureParser
- (instancetype)init {
self = [super self];
if (self) {
self.pipe = NSPipe.pipe;
self.results = [NSMutableDictionary new];
let pattern = [NSString stringWithFormat:#"[^']+'\\S+\\s([^\\]]+)\\]'\\smeasured\\s\\[Time,\\sseconds\\]\\saverage:\\s([^,]+)"];
NSError* error = nil;
self.regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
return nil;
}
}
return self;
}
- (void)capture:(void (^)(void))block {
// Save original output
int original = dup(STDERR_FILENO);
setvbuf(stderr, nil, _IONBF, 0);
dup2(self.pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO);
__weak let wself = self;
self.pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *handle) {
var *str = [[NSString alloc] initWithData:handle.availableData encoding:NSUTF8StringEncoding];
let firstMatch = [wself.regex firstMatchInString:str options:NSMatchingReportCompletion range:NSMakeRange(0, str.length)];
if (firstMatch) {
let name = [str substringWithRange:[firstMatch rangeAtIndex:1]];
let average = [str substringWithRange:[firstMatch rangeAtIndex:2]];
wself.results[name] = average;
}
// Print to stdout because stderr is piped
printf("%s", [str cStringUsingEncoding:NSUTF8StringEncoding]);
};
block();
// Revert
fflush(stderr);
dup2(original, STDERR_FILENO);
close(original);
}
#end
How to use:
- (void)testPerformanceExample {
let measureParser = [MeasureParser new];
[measureParser capture:^{
[self measureBlock:^{
// Put the code you want to measure the time of here.
sleep(1);
}];
}];
NSLog(#"%#", measureParser.results);
}
// Outputs
{
testPerformanceExample = "1.001";
}
Swift 5 version
final class MeasureParser {
let pipe: Pipe = Pipe()
let regex: NSRegularExpression?
let results: NSMutableDictionary = NSMutableDictionary()
init() {
self.regex = try? NSRegularExpression(
pattern: "\\[(Clock Monotonic Time|CPU Time|Memory Peak Physical|Memory Physical|CPU Instructions Retired|Disk Logical Writes|CPU Cycles), (s|kB|kI|kC)\\] average: ([0-9\\.]*),",
options: .caseInsensitive)
}
func capture(completion: #escaping () -> Void) {
let original = dup(STDERR_FILENO)
setvbuf(stderr, nil, _IONBF, 0)
dup2(self.pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO)
self.pipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
guard self != nil else { return }
let data = handle.availableData
let str = String(data: data, encoding: .utf8) ?? "<Non-ascii data of size\(data.count)>\n"
self!.fetchAndSaveMetrics(str)
// Print to stdout because stderr is piped
if let copy = (str as NSString?)?.cString(using: String.Encoding.utf8.rawValue) {
print("\(copy)")
}
}
completion()
fflush(stderr)
dup2(original, STDERR_FILENO)
close(original)
}
private func fetchAndSaveMetrics(_ str: String) {
guard let mRegex = self.regex else { return }
let matches = mRegex.matches(in: str, options: .reportCompletion, range: NSRange(location: 0, length: str.count))
matches.forEach {
let nameIndex = Range($0.range(at: 1), in: str)
let averageIndex = Range($0.range(at: 3), in: str)
if nameIndex != nil && averageIndex != nil {
let name = str[nameIndex!]
let average = str[averageIndex!]
self.results[name] = average
}
}
}
}
How to use it:
import XCTest
final class MyUiTests: XCTestCase {
var app: XCUIApplication!
let measureParser = MeasureParser()
// MARK: - XCTestCase
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
override func tearDown() {
//FIXME: Just for debugging
print(self.measureParser.results)
print(self.measureParser.results["CPU Cycles"])
print(self.measureParser.results["CPU Instructions Retired"])
print(self.measureParser.results["CPU Time"])
print(self.measureParser.results["Clock Monotonic Time"])
print(self.measureParser.results["Disk Logical Writes"])
print(self.measureParser.results["Memory Peak Physical"])
print(self.measureParser.results["Memory Physical"])
}
// MARK: - Tests
func testListing() {
self.measureParser.capture { [weak self] in
guard let self = self else { return }
self.measureListingScroll()
}
}
// MARK: XCTest measures
private func measureListingScroll() {
measure(metrics: [XCTCPUMetric(), XCTClockMetric(), XCTMemoryMetric(), XCTStorageMetric()]) {
self.app.swipeUp()
self.app.swipeUp()
self.app.swipeUp()
}
}
}
There's a private instance variable __perfMetricsForID of XCTestCase store the result.
And you can access it by call
NSDictionary* perfMetrics = [testCase valueForKey:#"__perfMetricsForID"];
the result is just like this:
I need to integrate my iOS app to consume a Web service requires a client certificate. I am using the below code to extract the private key from p12 in X509 certificate.
I found this code but not working, I translated it to swift 3.
func privateKeyFromCertificate() -> SecKeyRef {
let certName : String = //name of the certificate//
let resourcePath: String = NSBundle.mainBundle().pathForResource(certName, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "password_for_certificate"]
var privateKeyRef: SecKeyRef? = nil
var items : CFArray?
let securityError: OSStatus = SecPKCS12Import(p12Data, options, &items)
//let description : CFString = CFCopyDescription(items)
//print(description)
let theArray : CFArray = items!
if securityError == noErr && CFArrayGetCount(theArray) > 0 {
let newArray = theArray as [AnyObject] as NSArray
let dictionary = newArray.objectAtIndex(0)
let secIdentity = dictionary.valueForKey(kSecImportItemIdentity as String) as! SecIdentityRef
let securityError = SecIdentityCopyPrivateKey(secIdentity , &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}
return privateKeyRef!
}
The error encounter at :
let secIdentity = dictionary.valueForKey(kSecImportItemIdentity as String) as! SecIdentityRef
Below Updated version has the same problem at the same line:
func privateKeyFromCertificate() -> SecKey {
let certName : String = "certficateName"
let resourcePath: String = Bundle.main.path(forResource: certName, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "password_for_certificate"]
var privateKeyRef: SecKey? = nil
var items : CFArray?
let securityError: OSStatus = SecPKCS12Import(p12Data, options, &items)
//let description : CFString = CFCopyDescription(items)
//print(description)
let theArray : CFArray = items!
if securityError == noErr && CFArrayGetCount(theArray) > 0 {
let newArray = theArray as [AnyObject] as NSArray
let dictionary = newArray.object(at: 0)
//- encounter error here:
let secIdentity = (dictionary as AnyObject).value(kSecImportItemIdentity as String) as! SecIdentity
let securityError = SecIdentityCopyPrivateKey(secIdentity , &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}
return privateKeyRef!
}
I get the following error:
let secIdentity = (dictionary as AnyObject).value(kSecImportItemIdentity as String) as! SecIdentity
Cannot invoke 'Value' with any argument list of Type '(String)'
Your help is greatly appreciated.
Thanks
Can you try this for the error block - putting them in conditional id let block.
if let secIdentity = (dictionary as AnyObject).value(forKey: kSecImportItemIdentity as String) {
let securityError = SecIdentityCopyPrivateKey(secIdentity as! SecIdentity , &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}
I'm using this code for getting the Artwork, but it's not workout for me. What's the wrong in this code.Suggest me.
Thanks.
MPMediaQuery *mySongsQuery = [MPMediaQuery songsQuery];
NSArray *SongsList = [mySongsQuery collections];
for (MPMediaItemCollection *SongsArt in SongsList) {
NSArray *songs = [SongsArt items];
for (MPMediaItem *song in songs) {
if ([(MPMediaItem*)item valueForProperty:MPMediaItemPropertyAssetURL] != nil) {
CGSize artworkImageViewSize = CGSizeMake(40, 40);
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage * image = [artwork imageWithSize:artworkImageViewSize];
if (image!= nil)
{
imgv_songImageView.image = image;
}
else
{
imgv_songImageView.image = [UIImage imageNamed:#"musicD-jpeg.png"];
}
}
}
I assume you just want to loop through all songs in the music library so I don't see a need for collections:
MPMediaQuery *mySongsQuery = [MPMediaQuery songsQuery];
for (MPMediaItem *item in mySongsQuery.items) {
if (![[item valueForProperty:MPMediaItemPropertyIsCloudItem]boolValue]) {
CGSize artworkImageViewSize = CGSizeMake(40, 40);
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage *image = [artwork imageWithSize:artworkImageViewSize];
if (image) {
imgv_songImageView.image = image;
} else {
imgv_songImageView.image = [UIImage imageNamed:#"musicD-jpeg.png"];
}
}
}
I'm not sure why you want to check for the Asset URL but I've left it in.
Here i am posting the code to get the tracks and sorting them alphabetically. Its written in swift3.
/// Get all the songs in the device and display in the tableview
///
func getAllSongs() {
let query: MPMediaQuery = MPMediaQuery.songs()
let allSongs = query.items
allSongItems?.removeAll()
guard allSongs != nil else {
return
}
var index = 0
for item in allSongs! {
let pathURL: URL? = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
if pathURL == nil {
print("#Warning!!! Track : \(item) is not playable.")
} else {
let trackInfo = SongItem()
trackInfo.index = index
trackInfo.mediaItem = item
let title = item.value(forProperty: MPMediaItemPropertyTitle) as? String ?? "<Unknown>"
let artistName = item.value(forProperty: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
trackInfo.songName = title
trackInfo.artistName = artistName
trackInfo.isSelected = false
trackInfo.songURL = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
allSongItems?.append(trackInfo)
index += 1
}
}
// Sort the songs alphabetically
let sortedArray: [SongItem]? = allSongItems?.sorted {
$0.songName!.localizedCompare($1.songName!) == .orderedAscending
}
allSongItems?.removeAll()
if let arr = sortedArray {
allSongItems?.append(contentsOf: arr)
}
}
I have a MKMapView that has a UISearchBar on the top, and I want the user to be able to type a address, and to find that address and drop a pin on it. What I don't know is how to turn the address string into longitude and latitude, so I can make a CLLocation object. Does anyone know how I can do this?
You may find your answer in this question.
iOS - MKMapView place annotation by using address instead of lat / long By User Romes.
NSString *location = #"some address, state, and zip";
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
MKCoordinateRegion region = self.mapView.region;
region.center = placemark.region.center;
region.span.longitudeDelta /= 8.0;
region.span.latitudeDelta /= 8.0;
[self.mapView setRegion:region animated:YES];
[self.mapView addAnnotation:placemark];
}
}
];
A Very simple solution. But only applicable on iOS5.1 or later.
I used a similar approach like Vijay, but had to adjust one line of code. region.center = placemark.region.center didn't work for me. Maybe my code helps someone as well:
let location: String = "1 Infinite Loop, CA, USA"
let geocoder: CLGeocoder = CLGeocoder()
geocoder.geocodeAddressString(location,completionHandler: {(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if (placemarks?.count > 0) {
let topResult: CLPlacemark = (placemarks?[0])!
let placemark: MKPlacemark = MKPlacemark(placemark: topResult)
var region: MKCoordinateRegion = self.mapView.region
region.center.latitude = (placemark.location?.coordinate.latitude)!
region.center.longitude = (placemark.location?.coordinate.longitude)!
region.span = MKCoordinateSpanMake(0.5, 0.5)
self.mapView.setRegion(region, animated: true)
self.mapView.addAnnotation(placemark)
}
})
For swift2
var location: String = "some address, state, and zip"
var geocoder: CLGeocoder = CLGeocoder()
geocoder.geocodeAddressString(location,completionHandler: {(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if (placemarks?.count > 0) {
var topResult: CLPlacemark = (placemarks?[0])!
var placemark: MKPlacemark = MKPlacemark(placemark: topResult)
var region: MKCoordinateRegion = self.mapView.region
region.center = placemark.region.center
region.span.longitudeDelta /= 8.0
region.span.latitudeDelta /= 8.0
self.mapView.setRegion(region, animated: true)
self.mapView.addAnnotation(placemark)
}
})
func geoCodeUsingAddress(address: NSString) -> CLLocationCoordinate2D {
var latitude: Double = 0
var longitude: Double = 0
let addressstr : NSString = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=\(address)" as NSString
let urlStr = addressstr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let searchURL: NSURL = NSURL(string: urlStr! as String)!
do {
let newdata = try Data(contentsOf: searchURL as URL)
if let responseDictionary = try JSONSerialization.jsonObject(with: newdata, options: []) as? NSDictionary {
print(responseDictionary)
let array = responseDictionary.object(forKey: "results") as! NSArray
let dic = array[0] as! NSDictionary
let locationDic = (dic.object(forKey: "geometry") as! NSDictionary).object(forKey: "location") as! NSDictionary
latitude = locationDic.object(forKey: "lat") as! Double
longitude = locationDic.object(forKey: "lng") as! Double
} catch {
}
}
var center = CLLocationCoordinate2D()
center.latitude = latitude
center.longitude = longitude
return center
}